Merge "Revert "Add BMS as a Battery type""
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..c6f6251
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,6 @@
+filegroup {
+    name: "android_filesystem_config_header",
+    srcs: ["include/private/android_filesystem_config.h"],
+}
+
+subdirs = ["*"]
diff --git a/adb/Android.mk b/adb/Android.mk
index 16ed991..b444afa 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -81,17 +81,21 @@
 
 LIBADB_darwin_SRC_FILES := \
     sysdeps_unix.cpp \
-    usb_osx.cpp \
+    client/usb_dispatch.cpp \
+    client/usb_libusb.cpp \
+    client/usb_osx.cpp \
 
 LIBADB_linux_SRC_FILES := \
     sysdeps_unix.cpp \
-    usb_linux.cpp \
+    client/usb_dispatch.cpp \
+    client/usb_libusb.cpp \
+    client/usb_linux.cpp \
 
 LIBADB_windows_SRC_FILES := \
     sysdeps_win32.cpp \
     sysdeps/win32/errno.cpp \
     sysdeps/win32/stat.cpp \
-    usb_windows.cpp \
+    client/usb_windows.cpp \
 
 LIBADB_TEST_windows_SRCS := \
     sysdeps/win32/errno_test.cpp \
@@ -99,13 +103,26 @@
 
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
+LOCAL_MODULE := libadbd_usb
+LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
+LOCAL_SRC_FILES := daemon/usb.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
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
 LOCAL_MODULE := libadbd
 LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
 LOCAL_SRC_FILES := \
     $(LIBADB_SRC_FILES) \
     adbd_auth.cpp \
     jdwp_service.cpp \
-    usb_linux_client.cpp \
 
 LOCAL_SANITIZE := $(adb_target_sanitize)
 
@@ -113,6 +130,8 @@
 # this to take effect), this adds the includes to our path.
 LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase
 
+LOCAL_WHOLE_STATIC_LIBRARIES := libadbd_usb
+
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -135,6 +154,8 @@
 # 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_linux := libusb
+LOCAL_STATIC_LIBRARIES_darwin := libusb
 
 LOCAL_C_INCLUDES_windows := development/host/windows/usb/api/
 LOCAL_MULTILIB := first
@@ -154,7 +175,7 @@
     shell_service_test.cpp \
 
 LOCAL_SANITIZE := $(adb_target_sanitize)
-LOCAL_STATIC_LIBRARIES := libadbd libcrypto_utils libcrypto
+LOCAL_STATIC_LIBRARIES := libadbd libcrypto_utils libcrypto libusb
 LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
 include $(BUILD_NATIVE_TEST)
 
@@ -204,10 +225,13 @@
     libdiagnose_usb \
     libgmock_host \
 
+LOCAL_STATIC_LIBRARIES_linux := libusb
+LOCAL_STATIC_LIBRARIES_darwin := libusb
+
 # Set entrypoint to wmain from sysdeps_win32.cpp instead of main
 LOCAL_LDFLAGS_windows := -municode
 LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
-LOCAL_LDLIBS_darwin := -framework CoreFoundation -framework IOKit
+LOCAL_LDLIBS_darwin := -framework CoreFoundation -framework IOKit -lobjc
 LOCAL_LDLIBS_windows := -lws2_32 -luserenv
 LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
 
@@ -221,7 +245,7 @@
 
 LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
 
-LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon -lobjc
 
 # Use wmain instead of main
 LOCAL_LDFLAGS_windows := -municode
@@ -272,6 +296,9 @@
 LOCAL_STATIC_LIBRARIES_darwin := libcutils
 LOCAL_STATIC_LIBRARIES_linux := libcutils
 
+LOCAL_STATIC_LIBRARIES_darwin += libusb
+LOCAL_STATIC_LIBRARIES_linux += libusb
+
 LOCAL_CXX_STL := libc++_static
 
 # Don't add anything here, we don't want additional shared dependencies
@@ -330,18 +357,21 @@
 LOCAL_STATIC_LIBRARIES := \
     libadbd \
     libbase \
+    libbootloader_message \
     libfs_mgr \
     libfec \
     libfec_rs \
     libselinux \
     liblog \
-    libext4_utils_static \
+    libext4_utils \
     libsquashfs_utils \
     libcutils \
     libbase \
     libcrypto_utils \
     libcrypto \
     libminijail \
-    libdebuggerd_client \
+    libdebuggerd_handler \
 
 include $(BUILD_EXECUTABLE)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 3cd50ba..577e9b9 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -40,6 +40,7 @@
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/parsenetaddress.h>
+#include <android-base/quick_exit.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
@@ -1047,7 +1048,7 @@
         // may not read the OKAY sent above.
         adb_shutdown(reply_fd);
 
-        exit(0);
+        android::base::quick_exit(0);
     }
 
 #if ADB_HOST
@@ -1128,6 +1129,18 @@
         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);
+        }
+        SendOkay(reply_fd, FeatureSetToString(features));
+        return 0;
+    }
+#endif
+
     // remove TCP transport
     if (!strncmp(service, "disconnect:", 11)) {
         const std::string address(service + 11);
diff --git a/adb/adb.h b/adb/adb.h
index 19f09a3..aea5fb8 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -28,11 +28,14 @@
 #include "adb_trace.h"
 #include "fdevent.h"
 #include "socket.h"
+#include "usb.h"
 
 constexpr size_t MAX_PAYLOAD_V1 = 4 * 1024;
 constexpr size_t MAX_PAYLOAD_V2 = 256 * 1024;
 constexpr size_t MAX_PAYLOAD = MAX_PAYLOAD_V2;
 
+constexpr size_t LINUX_MAX_SOCKET_SIZE = 4194304;
+
 #define A_SYNC 0x434e5953
 #define A_CNXN 0x4e584e43
 #define A_OPEN 0x4e45504f
@@ -51,10 +54,9 @@
 std::string adb_version();
 
 // Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 38
+#define ADB_SERVER_VERSION 39
 
 class atransport;
-struct usb_handle;
 
 struct amessage {
     uint32_t command;     /* command identifier constant      */
@@ -195,18 +197,6 @@
 bool local_connect(int port);
 int  local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error);
 
-// USB host/client interface.
-void usb_init();
-int usb_write(usb_handle *h, const void *data, int len);
-int usb_read(usb_handle *h, void *data, int len);
-int usb_close(usb_handle *h);
-void usb_kick(usb_handle *h);
-
-// USB device detection.
-#if ADB_HOST
-int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol);
-#endif
-
 ConnectionState connection_state(atransport *t);
 
 extern const char* adb_device_banner;
@@ -218,8 +208,6 @@
 #define CHUNK_SIZE (64*1024)
 
 #if !ADB_HOST
-#define USB_ADB_PATH     "/dev/android_adb"
-
 #define USB_FFS_ADB_PATH  "/dev/usb-ffs/adb/"
 #define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH#x
 
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index ff2d76d..c3f1fe0 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -207,11 +207,6 @@
     }
 
     if (S_ISREG(st.st_mode)) {
-        if (!android::base::EndsWith(path, ".adb_key")) {
-            LOG(INFO) << "skipping non-adb_key '" << path << "'";
-            return false;
-        }
-
         return read_key_file(path);
     } else if (S_ISDIR(st.st_mode)) {
         if (!allow_dir) {
@@ -236,7 +231,12 @@
                 continue;
             }
 
-            result |= read_keys((path + OS_PATH_SEPARATOR + name).c_str(), false);
+            if (!android::base::EndsWith(name, ".adb_key")) {
+                LOG(INFO) << "skipping non-adb_key '" << path << "/" << name << "'";
+                continue;
+            }
+
+            result |= read_key_file((path + OS_PATH_SEPARATOR + name));
         }
         return result;
     }
@@ -388,7 +388,13 @@
 
 static void adb_auth_inotify_init(const std::set<std::string>& paths) {
     LOG(INFO) << "adb_auth_inotify_init...";
+
     int infd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
+    if (infd < 0) {
+        PLOG(ERROR) << "failed to create inotify fd";
+        return;
+    }
+
     for (const std::string& path : paths) {
         int wd = inotify_add_watch(infd, path.c_str(), IN_CREATE | IN_MOVED_TO);
         if (wd < 0) {
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index 002d061..c369d60 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -109,8 +109,8 @@
     }
 
     std::unordered_map<std::string, int> trace_flags = {
-        {"1", 0},
-        {"all", 0},
+        {"1", -1},
+        {"all", -1},
         {"adb", ADB},
         {"sockets", SOCKETS},
         {"packets", PACKETS},
@@ -133,8 +133,8 @@
             continue;
         }
 
-        if (flag->second == 0) {
-            // 0 is used for the special values "1" and "all" that enable all
+        if (flag->second == -1) {
+            // -1 is used for the special values "1" and "all" that enable all
             // tracing.
             adb_trace_mask = ~0;
             return;
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index 5206a99..aaffa29 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -58,4 +58,8 @@
 void adb_trace_init(char**);
 void adb_trace_enable(AdbTrace trace_tag);
 
+#define ATRACE_TAG ATRACE_TAG_ADB
+#include <cutils/trace.h>
+#include <utils/Trace.h>
+
 #endif /* __ADB_TRACE_H */
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 5a3b401..7058acb 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -19,16 +19,15 @@
 #include "adb_utils.h"
 #include "adb_unique_fd.h"
 
-#include <libgen.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include <algorithm>
-#include <mutex>
 #include <vector>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
@@ -100,52 +99,6 @@
   return result;
 }
 
-std::string adb_basename(const std::string& path) {
-  static std::mutex& basename_lock = *new std::mutex();
-
-  // Copy path because basename may modify the string passed in.
-  std::string result(path);
-
-  // Use lock because basename() may write to a process global and return a
-  // pointer to that. Note that this locking strategy only works if all other
-  // callers to basename in the process also grab this same lock.
-  std::lock_guard<std::mutex> lock(basename_lock);
-
-  // Note that if std::string uses copy-on-write strings, &str[0] will cause
-  // the copy to be made, so there is no chance of us accidentally writing to
-  // the storage for 'path'.
-  char* name = basename(&result[0]);
-
-  // In case dirname returned a pointer to a process global, copy that string
-  // before leaving the lock.
-  result.assign(name);
-
-  return result;
-}
-
-std::string adb_dirname(const std::string& path) {
-  static std::mutex& dirname_lock = *new std::mutex();
-
-  // Copy path because dirname may modify the string passed in.
-  std::string result(path);
-
-  // Use lock because dirname() may write to a process global and return a
-  // pointer to that. Note that this locking strategy only works if all other
-  // callers to dirname in the process also grab this same lock.
-  std::lock_guard<std::mutex> lock(dirname_lock);
-
-  // Note that if std::string uses copy-on-write strings, &str[0] will cause
-  // the copy to be made, so there is no chance of us accidentally writing to
-  // the storage for 'path'.
-  char* parent = dirname(&result[0]);
-
-  // In case dirname returned a pointer to a process global, copy that string
-  // before leaving the lock.
-  result.assign(parent);
-
-  return result;
-}
-
 // Given a relative or absolute filepath, create the directory hierarchy
 // as needed. Returns true if the hierarchy is/was setup.
 bool mkdirs(const std::string& path) {
@@ -171,7 +124,7 @@
     return true;
   }
 
-  const std::string parent(adb_dirname(path));
+  const std::string parent(android::base::Dirname(path));
 
   // If dirname returned the same path as what we passed in, don't go recursive.
   // This can happen on Windows when walking up the directory hierarchy and not
@@ -313,3 +266,15 @@
 void AdbCloser::Close(int fd) {
     adb_close(fd);
 }
+
+int usage(const char* fmt, ...) {
+    fprintf(stderr, "adb: ");
+
+    va_list ap;
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+
+    fprintf(stderr, "\n");
+    return 1;
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 16317e0..e0ad103 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -21,16 +21,13 @@
 
 #include <android-base/macros.h>
 
+int usage(const char*, ...);
+
 void close_stdin();
 
 bool getcwd(std::string* cwd);
 bool directory_exists(const std::string& path);
 
-// Like the regular basename and dirname, but thread-safe on all
-// platforms and capable of correctly handling exotic Windows paths.
-std::string adb_basename(const std::string& path);
-std::string adb_dirname(const std::string& path);
-
 // Return the user's home directory.
 std::string adb_get_homedir_path();
 
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 4cac485..a3bc445 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -109,18 +109,6 @@
   ASSERT_EQ(R"('abc)')", escape_arg("abc)"));
 }
 
-TEST(adb_utils, adb_basename) {
-  EXPECT_EQ("sh", adb_basename("/system/bin/sh"));
-  EXPECT_EQ("sh", adb_basename("sh"));
-  EXPECT_EQ("sh", adb_basename("/system/bin/sh/"));
-}
-
-TEST(adb_utils, adb_dirname) {
-  EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh"));
-  EXPECT_EQ(".", adb_dirname("sh"));
-  EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh/"));
-}
-
 void test_mkdirs(const std::string& basepath) {
   // Test creating a directory hierarchy.
   ASSERT_TRUE(mkdirs(basepath));
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index 9b59d05..a5c312b 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -21,6 +21,7 @@
 #include <string>
 #include <vector>
 
+#include <android-base/file.h>
 #include <android-base/strings.h>
 
 #include "sysdeps.h"
@@ -113,14 +114,14 @@
 
   private:
     void SetLineMessage(const std::string& action) {
-        line_message_ = action + " " + adb_basename(dest_file_);
+        line_message_ = action + " " + android::base::Basename(dest_file_);
     }
 
     void SetSrcFile(const std::string path) {
         src_file_ = path;
         if (!dest_dir_.empty()) {
             // Only uses device-provided name when user passed a directory.
-            dest_file_ = adb_basename(path);
+            dest_file_ = android::base::Basename(path);
             SetLineMessage("generating");
         }
     }
@@ -182,11 +183,8 @@
     DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
 };
 
-// Implemented in commandline.cpp
-int usage();
-
 int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
-    if (argc > 2) return usage();
+    if (argc > 2) return usage("usage: 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 4ec0fc2..97a54fd 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -23,9 +23,12 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+#include <thread>
+
 #include <android-base/errors.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/quick_exit.h>
 #include <android-base/stringprintf.h>
 
 #include "adb.h"
@@ -33,6 +36,7 @@
 #include "adb_listeners.h"
 #include "adb_utils.h"
 #include "commandline.h"
+#include "sysdeps/chrono.h"
 #include "transport.h"
 
 static std::string GetLogFilePath() {
@@ -83,7 +87,7 @@
 static BOOL WINAPI ctrlc_handler(DWORD type) {
     // TODO: Consider trying to kill a starting up adb server (if we're in
     // launch_server) by calling GenerateConsoleCtrlEvent().
-    exit(STATUS_CONTROL_C_EXIT);
+    android::base::quick_exit(STATUS_CONTROL_C_EXIT);
     return TRUE;
 }
 #endif
@@ -105,6 +109,10 @@
     }
 
     SetConsoleCtrlHandler(ctrlc_handler, TRUE);
+#else
+    signal(SIGINT, [](int) {
+        android::base::quick_exit(0);
+    });
 #endif
 
     init_transport_registration();
@@ -113,8 +121,18 @@
     local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
 
     std::string error;
-    if (install_listener(socket_spec, "*smartsocket*", nullptr, 0, nullptr, &error)) {
-        fatal("could not install *smartsocket* listener: %s", error.c_str());
+
+    auto start = std::chrono::steady_clock::now();
+
+    // If we told a previous adb server to quit because of version mismatch, we can get to this
+    // point before it's finished exiting. Retry for a while to give it some time.
+    while (install_listener(socket_spec, "*smartsocket*", nullptr, 0, nullptr, &error) !=
+           INSTALL_STATUS_OK) {
+        if (std::chrono::steady_clock::now() - start > 0.5s) {
+            fatal("could not install *smartsocket* listener: %s", error.c_str());
+        }
+
+        std::this_thread::sleep_for(100ms);
     }
 
     if (is_daemon) {
diff --git a/adb/client/usb_dispatch.cpp b/adb/client/usb_dispatch.cpp
new file mode 100644
index 0000000..bfc8e16
--- /dev/null
+++ b/adb/client/usb_dispatch.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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/logging.h>
+#include "usb.h"
+
+void usb_init() {
+    if (should_use_libusb()) {
+        LOG(DEBUG) << "using libusb backend";
+        libusb::usb_init();
+    } else {
+        LOG(DEBUG) << "using native backend";
+        native::usb_init();
+    }
+}
+
+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)
+               : native::usb_write(reinterpret_cast<native::usb_handle*>(h), data, len);
+}
+
+int usb_read(usb_handle* h, void* data, int len) {
+    return should_use_libusb()
+               ? libusb::usb_read(reinterpret_cast<libusb::usb_handle*>(h), data, len)
+               : native::usb_read(reinterpret_cast<native::usb_handle*>(h), data, len);
+}
+
+int usb_close(usb_handle* h) {
+    return should_use_libusb() ? libusb::usb_close(reinterpret_cast<libusb::usb_handle*>(h))
+                               : native::usb_close(reinterpret_cast<native::usb_handle*>(h));
+}
+
+void usb_kick(usb_handle* h) {
+    should_use_libusb() ? libusb::usb_kick(reinterpret_cast<libusb::usb_handle*>(h))
+                        : native::usb_kick(reinterpret_cast<native::usb_handle*>(h));
+}
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
new file mode 100644
index 0000000..7adb262
--- /dev/null
+++ b/adb/client/usb_libusb.cpp
@@ -0,0 +1,519 @@
+/*
+ * 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 "usb.h"
+
+#include "sysdeps.h"
+
+#include <stdint.h>
+
+#include <atomic>
+#include <chrono>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <unordered_map>
+
+#include <libusb/libusb.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/quick_exit.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "adb.h"
+#include "transport.h"
+#include "usb.h"
+
+using namespace std::literals;
+
+using android::base::StringPrintf;
+
+// RAII wrappers for libusb.
+struct ConfigDescriptorDeleter {
+    void operator()(libusb_config_descriptor* desc) {
+        libusb_free_config_descriptor(desc);
+    }
+};
+
+using unique_config_descriptor = std::unique_ptr<libusb_config_descriptor, ConfigDescriptorDeleter>;
+
+struct DeviceHandleDeleter {
+    void operator()(libusb_device_handle* h) {
+        libusb_close(h);
+    }
+};
+
+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() {
+        libusb_free_transfer(transfer);
+    }
+
+    const char* name;
+    libusb_transfer* transfer;
+    bool transfer_complete;
+    std::condition_variable cv;
+    std::mutex mutex;
+    uint16_t zero_mask;
+
+    void Notify() {
+        LOG(DEBUG) << "notifying " << name << " transfer complete";
+        transfer_complete = true;
+        cv.notify_one();
+    }
+};
+
+namespace libusb {
+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)
+        : device_address(device_address),
+          serial(serial),
+          closing(false),
+          device_handle(device_handle.release()),
+          read("read", zero_mask),
+          write("write", zero_mask),
+          interface(interface),
+          bulk_in(bulk_in),
+          bulk_out(bulk_out) {
+    }
+
+    ~usb_handle() {
+        Close();
+    }
+
+    void Close() {
+        std::unique_lock<std::mutex> lock(device_handle_mutex);
+        // Cancelling transfers will trigger more Closes, so make sure this only happens once.
+        if (closing) {
+            return;
+        }
+        closing = true;
+
+        // Make sure that no new transfers come in.
+        libusb_device_handle* handle = device_handle;
+        if (!handle) {
+            return;
+        }
+
+        device_handle = nullptr;
+
+        // Cancel already dispatched transfers.
+        libusb_cancel_transfer(read.transfer);
+        libusb_cancel_transfer(write.transfer);
+
+        libusb_release_interface(handle, interface);
+        libusb_close(handle);
+    }
+
+    std::string device_address;
+    std::string serial;
+
+    std::atomic<bool> closing;
+    std::mutex device_handle_mutex;
+    libusb_device_handle* device_handle;
+
+    transfer_info read;
+    transfer_info write;
+
+    uint8_t interface;
+    uint8_t bulk_in;
+    uint8_t bulk_out;
+};
+
+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 std::string get_device_address(libusb_device* device) {
+    return StringPrintf("usb:%d:%d", libusb_get_bus_number(device),
+                        libusb_get_device_address(device));
+}
+
+static bool endpoint_is_output(uint8_t endpoint) {
+    return (endpoint & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT;
+}
+
+static bool should_perform_zero_transfer(uint8_t endpoint, size_t write_length, uint16_t zero_mask) {
+    return endpoint_is_output(endpoint) && write_length != 0 && zero_mask != 0 &&
+           (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);
+
+        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);
+    }
+}
+
+void usb_init() {
+    LOG(DEBUG) << "initializing libusb...";
+    int rc = libusb_init(nullptr);
+    if (rc != 0) {
+        LOG(FATAL) << "failed to initialize libusb: " << libusb_error_name(rc);
+    }
+
+    // Spawn a thread for libusb_handle_events.
+    std::thread([]() {
+        adb_thread_setname("libusb");
+        while (true) {
+            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();
+    });
+}
+
+// Dispatch a libusb transfer, unlock |device_lock|, and then wait for the result.
+static int perform_usb_transfer(usb_handle* h, transfer_info* info,
+                                std::unique_lock<std::mutex> device_lock) {
+    libusb_transfer* transfer = info->transfer;
+
+    transfer->user_data = info;
+    transfer->callback = [](libusb_transfer* transfer) {
+        transfer_info* info = static_cast<transfer_info*>(transfer->user_data);
+
+        LOG(DEBUG) << info->name << " transfer callback entered";
+
+        // Make sure that the original submitter has made it to the condition_variable wait.
+        std::unique_lock<std::mutex> lock(info->mutex);
+
+        LOG(DEBUG) << info->name << " callback successfully acquired lock";
+
+        if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+            LOG(WARNING) << info->name
+                         << " transfer failed: " << libusb_error_name(transfer->status);
+            info->Notify();
+            return;
+        }
+
+        if (transfer->actual_length != transfer->length) {
+            LOG(DEBUG) << info->name << " transfer incomplete, resubmitting";
+            transfer->length -= transfer->actual_length;
+            transfer->buffer += transfer->actual_length;
+            int rc = libusb_submit_transfer(transfer);
+            if (rc != 0) {
+                LOG(WARNING) << "failed to submit " << info->name
+                             << " transfer: " << libusb_error_name(rc);
+                transfer->status = LIBUSB_TRANSFER_ERROR;
+                info->Notify();
+            }
+            return;
+        }
+
+        if (should_perform_zero_transfer(transfer->endpoint, transfer->length, info->zero_mask)) {
+            LOG(DEBUG) << "submitting zero-length write";
+            transfer->length = 0;
+            int rc = libusb_submit_transfer(transfer);
+            if (rc != 0) {
+                LOG(WARNING) << "failed to submit zero-length write: " << libusb_error_name(rc);
+                transfer->status = LIBUSB_TRANSFER_ERROR;
+                info->Notify();
+            }
+            return;
+        }
+
+        LOG(VERBOSE) << info->name << "transfer fully complete";
+        info->Notify();
+    };
+
+    LOG(DEBUG) << "locking " << info->name << " transfer_info mutex";
+    std::unique_lock<std::mutex> lock(info->mutex);
+    info->transfer_complete = false;
+    LOG(DEBUG) << "submitting " << info->name << " transfer";
+    int rc = libusb_submit_transfer(transfer);
+    if (rc != 0) {
+        LOG(WARNING) << "failed to submit " << info->name << " transfer: " << libusb_error_name(rc);
+        errno = EIO;
+        return -1;
+    }
+
+    LOG(DEBUG) << info->name << " transfer successfully submitted";
+    device_lock.unlock();
+    info->cv.wait(lock, [info]() { return info->transfer_complete; });
+    if (transfer->status != 0) {
+        errno = EIO;
+        return -1;
+    }
+
+    return 0;
+}
+
+int usb_write(usb_handle* h, const void* d, int len) {
+    LOG(DEBUG) << "usb_write of length " << len;
+
+    std::unique_lock<std::mutex> lock(h->device_handle_mutex);
+    if (!h->device_handle) {
+        errno = EIO;
+        return -1;
+    }
+
+    transfer_info* info = &h->write;
+    info->transfer->dev_handle = h->device_handle;
+    info->transfer->flags = 0;
+    info->transfer->endpoint = h->bulk_out;
+    info->transfer->type = LIBUSB_TRANSFER_TYPE_BULK;
+    info->transfer->length = len;
+    info->transfer->buffer = reinterpret_cast<unsigned char*>(const_cast<void*>(d));
+    info->transfer->num_iso_packets = 0;
+
+    int rc = perform_usb_transfer(h, info, std::move(lock));
+    LOG(DEBUG) << "usb_write(" << len << ") = " << rc;
+    return rc;
+}
+
+int usb_read(usb_handle* h, void* d, int len) {
+    LOG(DEBUG) << "usb_read of length " << len;
+
+    std::unique_lock<std::mutex> lock(h->device_handle_mutex);
+    if (!h->device_handle) {
+        errno = EIO;
+        return -1;
+    }
+
+    transfer_info* info = &h->read;
+    info->transfer->dev_handle = h->device_handle;
+    info->transfer->flags = 0;
+    info->transfer->endpoint = h->bulk_in;
+    info->transfer->type = LIBUSB_TRANSFER_TYPE_BULK;
+    info->transfer->length = len;
+    info->transfer->buffer = reinterpret_cast<unsigned char*>(d);
+    info->transfer->num_iso_packets = 0;
+
+    int rc = perform_usb_transfer(h, info, std::move(lock));
+    LOG(DEBUG) << "usb_read(" << len << ") = " << rc;
+    return rc;
+}
+
+int usb_close(usb_handle* h) {
+    std::unique_lock<std::mutex> lock(usb_handles_mutex);
+    auto it = usb_handles.find(h->device_address);
+    if (it == usb_handles.end()) {
+        LOG(FATAL) << "attempted to close unregistered usb_handle for '" << h->serial << "'";
+    }
+    usb_handles.erase(h->device_address);
+    return 0;
+}
+
+void usb_kick(usb_handle* h) {
+    h->Close();
+}
+} // namespace libusb
diff --git a/adb/usb_linux.cpp b/adb/client/usb_linux.cpp
similarity index 99%
rename from adb/usb_linux.cpp
rename to adb/client/usb_linux.cpp
index e7f1338..13b7674 100644
--- a/adb/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -46,6 +46,7 @@
 
 #include "adb.h"
 #include "transport.h"
+#include "usb.h"
 
 using namespace std::chrono_literals;
 using namespace std::literals;
@@ -53,7 +54,8 @@
 /* usb scan debugging is waaaay too verbose */
 #define DBGX(x...)
 
-struct usb_handle {
+namespace native {
+struct usb_handle : public ::usb_handle {
     ~usb_handle() {
       if (fd != -1) unix_close(fd);
     }
@@ -595,3 +597,4 @@
         fatal_errno("cannot create device_poll thread");
     }
 }
+} // namespace native
diff --git a/adb/usb_osx.cpp b/adb/client/usb_osx.cpp
similarity index 98%
rename from adb/usb_osx.cpp
rename to adb/client/usb_osx.cpp
index e541f6e..d4fc7c0 100644
--- a/adb/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -44,6 +44,7 @@
 
 using namespace std::chrono_literals;
 
+namespace native {
 struct usb_handle
 {
     UInt8 bulkIn;
@@ -298,7 +299,8 @@
         usb_handle* handle_p = handle.get();
         VLOG(USB) << "Add usb device " << serial;
         AddDevice(std::move(handle));
-        register_usb_transport(handle_p, serial, devpath.c_str(), 1);
+        register_usb_transport(reinterpret_cast<::usb_handle*>(handle_p), serial, devpath.c_str(),
+                               1);
     }
 }
 
@@ -558,3 +560,4 @@
     std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
     usb_kick_locked(handle);
 }
+} // namespace native
diff --git a/adb/usb_windows.cpp b/adb/client/usb_windows.cpp
similarity index 100%
rename from adb/usb_windows.cpp
rename to adb/client/usb_windows.cpp
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index a064de2..3de7be6 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -91,9 +91,9 @@
         "global options:\n"
         " -a         listen on all network interfaces, not just localhost\n"
         " -d         use USB device (error if multiple devices connected)\n"
-        " -e         use emulator (error if multiple emulators running)\n"
+        " -e         use TCP/IP device (error if multiple TCP/IP devices available)\n"
         " -s SERIAL\n"
-        "     use device/emulator with given serial number (overrides $ANDROID_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"
@@ -231,11 +231,6 @@
     // clang-format on
 }
 
-int usage() {
-    help();
-    return 1;
-}
-
 #if defined(_WIN32)
 
 // Implemented in sysdeps_win32.cpp.
@@ -1235,7 +1230,7 @@
 }
 
 static int restore(int argc, const char** argv) {
-    if (argc != 2) return usage();
+    if (argc != 2) return usage("restore requires an argument");
 
     const char* filename = argv[1];
     int tarFd = adb_open(filename, O_RDONLY);
@@ -1443,19 +1438,19 @@
             /* 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();
+            if (argc < 2) return usage("--reply-fd requires an argument");
             const char* reply_fd_str = argv[1];
             argc--;
             argv++;
             ack_reply_fd = strtol(reply_fd_str, nullptr, 10);
             if (!_is_valid_ack_reply_fd(ack_reply_fd)) {
                 fprintf(stderr, "adb: invalid reply fd \"%s\"\n", reply_fd_str);
-                return usage();
+                return 1;
             }
         } else if (!strncmp(argv[0], "-p", 2)) {
             const char* product = nullptr;
             if (argv[0][2] == '\0') {
-                if (argc < 2) return usage();
+                if (argc < 2) return usage("-p requires an argument");
                 product = argv[1];
                 argc--;
                 argv++;
@@ -1465,13 +1460,13 @@
             gProductOutPath = find_product_out_path(product);
             if (gProductOutPath.empty()) {
                 fprintf(stderr, "adb: could not resolve \"-p %s\"\n", product);
-                return usage();
+                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();
+                if (argc < 2 || argv[0][2] != '\0') return usage("-s requires an argument");
                 serial = argv[1];
                 argc--;
                 argv++;
@@ -1484,7 +1479,7 @@
             gListenAll = 1;
         } else if (!strncmp(argv[0], "-H", 2)) {
             if (argv[0][2] == '\0') {
-                if (argc < 2) return usage();
+                if (argc < 2) return usage("-H requires an argument");
                 server_host_str = argv[1];
                 argc--;
                 argv++;
@@ -1493,7 +1488,7 @@
             }
         } else if (!strncmp(argv[0], "-P", 2)) {
             if (argv[0][2] == '\0') {
-                if (argc < 2) return usage();
+                if (argc < 2) return usage("-P requires an argument");
                 server_port_str = argv[1];
                 argc--;
                 argv++;
@@ -1501,7 +1496,7 @@
                 server_port_str = argv[0] + 2;
             }
         } else if (!strcmp(argv[0], "-L")) {
-            if (argc < 2) return usage();
+            if (argc < 2) return usage("-L requires an argument");
             server_socket_str = argv[1];
             argc--;
             argv++;
@@ -1566,7 +1561,7 @@
         if (no_daemon || is_daemon) {
             if (is_daemon && (ack_reply_fd == -1)) {
                 fprintf(stderr, "reply fd for adb server to client communication not specified.\n");
-                return usage();
+                return 1;
             }
             r = adb_server_main(is_daemon, server_socket_str, ack_reply_fd);
         } else {
@@ -1579,7 +1574,8 @@
     }
 
     if (argc == 0) {
-        return usage();
+        help();
+        return 1;
     }
 
     /* handle wait-for-* prefix */
@@ -1696,7 +1692,7 @@
         }
     }
     else if (!strcmp(argv[0], "sideload")) {
-        if (argc != 2) return usage();
+        if (argc != 2) return usage("sideload requires an argument");
         if (adb_sideload_host(argv[1])) {
             return 1;
         } else {
@@ -1730,7 +1726,7 @@
         bool reverse = !strcmp(argv[0], "reverse");
         ++argv;
         --argc;
-        if (argc < 1) return usage();
+        if (argc < 1) return usage("%s requires an argument", argv[0]);
 
         // Determine the <host-prefix> for this command.
         std::string host_prefix;
@@ -1750,24 +1746,24 @@
 
         std::string cmd, error;
         if (strcmp(argv[0], "--list") == 0) {
-            if (argc != 1) return usage();
+            if (argc != 1) return usage("--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();
+            if (argc != 1) return usage("--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();
+            if (argc != 2) return usage("--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();
+            if (argc != 3) return usage("--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();
+            if (argc != 2) return usage("forward takes two arguments");
             if (forward_targets_are_valid(argv[0], argv[1], &error)) {
                 cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
             }
@@ -1796,7 +1792,7 @@
     }
     /* do_sync_*() commands */
     else if (!strcmp(argv[0], "ls")) {
-        if (argc != 2) return usage();
+        if (argc != 2) return usage("ls requires an argument");
         return do_sync_ls(argv[1]) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "push")) {
@@ -1805,7 +1801,7 @@
         const char* dst = nullptr;
 
         parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
-        if (srcs.empty() || !dst) return usage();
+        if (srcs.empty() || !dst) return usage("push requires an argument");
         return do_sync_push(srcs, dst) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "pull")) {
@@ -1814,22 +1810,22 @@
         const char* dst = ".";
 
         parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
-        if (srcs.empty()) return usage();
+        if (srcs.empty()) return usage("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();
+        if (argc < 2) return usage("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();
+        if (argc < 2) return usage("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();
+        if (argc < 2) return usage("uninstall requires an argument");
         if (_use_legacy_install()) {
             return uninstall_app_legacy(transport_type, serial, argc, argv);
         }
@@ -1852,12 +1848,12 @@
             // A local path or "android"/"data" arg was specified.
             src = argv[1];
         } else {
-            return usage();
+            return usage("usage: adb sync [-l] [PARTITION]");
         }
 
         if (src != "" &&
             src != "system" && src != "data" && src != "vendor" && src != "oem") {
-            return usage();
+            return usage("don't know how to sync %s partition", src.c_str());
         }
 
         std::string system_src_path = product_file("system");
@@ -1909,7 +1905,7 @@
         return restore(argc, argv);
     }
     else if (!strcmp(argv[0], "keygen")) {
-        if (argc < 2) return usage();
+        if (argc != 2) return usage("keygen requires an argument");
         // Always print key generation information for keygen command.
         adb_trace_enable(AUTH);
         return adb_auth_keygen(argv[1]);
@@ -1926,15 +1922,14 @@
 
 
     /* "adb /?" is a common idiom under Windows */
-    else if (!strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) {
+    else if (!strcmp(argv[0], "--help") || !strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) {
         help();
         return 0;
     }
-    else if (!strcmp(argv[0], "version")) {
+    else if (!strcmp(argv[0], "--version") || !strcmp(argv[0], "version")) {
         fprintf(stdout, "%s", adb_version().c_str());
         return 0;
-    }
-    else if (!strcmp(argv[0], "features")) {
+    } else if (!strcmp(argv[0], "features")) {
         // Only list the features common to both the adb client and the device.
         FeatureSet features;
         std::string error;
@@ -1949,6 +1944,8 @@
             }
         }
         return 0;
+    } else if (!strcmp(argv[0], "host-features")) {
+        return adb_query_command("host:host-features");
     } else if (!strcmp(argv[0], "reconnect")) {
         if (argc == 1) {
             return adb_query_command("host:reconnect");
@@ -1961,12 +1958,12 @@
                 std::string err;
                 return adb_query_command("host:reconnect-offline");
             } else {
-                return usage();
+                return usage("usage: adb reconnect [device|offline]");
             }
         }
     }
 
-    usage();
+    usage("unknown command %s", argv[0]);
     return 1;
 }
 
@@ -2118,7 +2115,8 @@
 
         std::string cmd = android::base::StringPrintf(
                 "%s install-write -S %" PRIu64 " %d %d_%s -",
-                install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), session_id, i, adb_basename(file).c_str());
+                install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), session_id, i,
+                android::base::Basename(file).c_str());
 
         int localFd = adb_open(file, O_RDONLY);
         if (localFd < 0) {
@@ -2236,7 +2234,7 @@
     int result = -1;
     std::vector<const char*> apk_file = {argv[last_apk]};
     std::string apk_dest = android::base::StringPrintf(
-        where, adb_basename(argv[last_apk]).c_str());
+        where, android::base::Basename(argv[last_apk]).c_str());
     if (!do_sync_push(apk_file, apk_dest.c_str())) 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/daemon/main.cpp b/adb/daemon/main.cpp
index 78434a0..6382b67 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -34,9 +34,9 @@
 #include <libminijail.h>
 #include <scoped_minijail.h>
 
-#include "debuggerd/client.h"
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
+#include "debuggerd/handler.h"
 #include "selinux/android.h"
 
 #include "adb.h"
@@ -170,7 +170,7 @@
     drop_privileges(server_port);
 
     bool is_usb = false;
-    if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) {
+    if (access(USB_FFS_ADB_EP0, F_OK) == 0) {
         // Listen on USB.
         usb_init();
         is_usb = true;
diff --git a/adb/usb_linux_client.cpp b/adb/daemon/usb.cpp
similarity index 68%
rename from adb/usb_linux_client.cpp
rename to adb/daemon/usb.cpp
index 1cc7f68..c08b922 100644
--- a/adb/usb_linux_client.cpp
+++ b/adb/daemon/usb.cpp
@@ -40,49 +40,34 @@
 #include <android-base/properties.h>
 
 #include "adb.h"
+#include "daemon/usb.h"
 #include "transport.h"
 
 using namespace std::chrono_literals;
 
-#define MAX_PACKET_SIZE_FS	64
-#define MAX_PACKET_SIZE_HS	512
-#define MAX_PACKET_SIZE_SS	1024
+#define MAX_PACKET_SIZE_FS 64
+#define MAX_PACKET_SIZE_HS 512
+#define MAX_PACKET_SIZE_SS 1024
 
-// Writes larger than 16k fail on some devices (seed with 3.10.49-g209ea2f in particular).
-#define USB_FFS_MAX_WRITE 16384
+// Kernels before 3.3 have a 16KiB transfer limit  That limit was replaced
+// with a 16MiB global limit in 3.3, but each URB submitted required a
+// contiguous kernel allocation, so you would get ENOMEM if you tried to
+// send something larger than the biggest available contiguous kernel
+// memory region. Large contiguous allocations could be unreliable
+// on a device kernel that has been running for a while fragmenting its
+// memory so we start with a larger allocation, and shrink the amount if
+// necessary.
+#define USB_FFS_BULK_SIZE 16384
 
-// The kernel allocates a contiguous buffer for reads, which can fail for large ones due to
-// fragmentation. 16k chosen arbitrarily to match the write limit.
-#define USB_FFS_MAX_READ 16384
+#define cpu_to_le16(x) htole16(x)
+#define cpu_to_le32(x) htole32(x)
 
-#define cpu_to_le16(x)  htole16(x)
-#define cpu_to_le32(x)  htole32(x)
+#define FUNCTIONFS_ENDPOINT_ALLOC       _IOR('g', 231, __u32)
+
+static constexpr size_t ENDPOINT_ALLOC_RETRIES = 2;
 
 static int dummy_fd = -1;
 
-struct usb_handle {
-    usb_handle() : kicked(false) {
-    }
-
-    std::condition_variable notify;
-    std::mutex lock;
-    std::atomic<bool> kicked;
-    bool open_new_connection = true;
-
-    int (*write)(usb_handle *h, const void *data, int len);
-    int (*read)(usb_handle *h, void *data, int len);
-    void (*kick)(usb_handle *h);
-    void (*close)(usb_handle *h);
-
-    // Legacy f_adb
-    int fd = -1;
-
-    // FunctionFS
-    int control = -1;
-    int bulk_out = -1; /* "out" from the host's perspective => source for adbd */
-    int bulk_in = -1;  /* "in" from the host's perspective => sink for adbd */
-};
-
 struct func_desc {
     struct usb_interface_descriptor intf;
     struct usb_endpoint_descriptor_no_audio source;
@@ -195,6 +180,7 @@
     .source_comp = {
         .bLength = sizeof(ss_descriptors.source_comp),
         .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+        .bMaxBurst = 4,
     },
     .sink = {
         .bLength = sizeof(ss_descriptors.sink),
@@ -206,6 +192,7 @@
     .sink_comp = {
         .bLength = sizeof(ss_descriptors.sink_comp),
         .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+        .bMaxBurst = 4,
     },
 };
 
@@ -226,7 +213,6 @@
     .Reserved = cpu_to_le32(0),
 };
 
-
 #define STR_INTERFACE_ "ADB Interface"
 
 static const struct {
@@ -248,144 +234,11 @@
     },
 };
 
-static void usb_adb_open_thread(void* x) {
-    struct usb_handle *usb = (struct usb_handle *)x;
-    int fd;
-
-    adb_thread_setname("usb open");
-
-    while (true) {
-        // wait until the USB device needs opening
-        std::unique_lock<std::mutex> lock(usb->lock);
-        while (!usb->open_new_connection) {
-            usb->notify.wait(lock);
-        }
-        usb->open_new_connection = false;
-        lock.unlock();
-
-        D("[ usb_thread - opening device ]");
-        do {
-            /* XXX use inotify? */
-            fd = unix_open("/dev/android_adb", O_RDWR);
-            if (fd < 0) {
-                // to support older kernels
-                fd = unix_open("/dev/android", O_RDWR);
-            }
-            if (fd < 0) {
-                std::this_thread::sleep_for(1s);
-            }
-        } while (fd < 0);
-        D("[ opening device succeeded ]");
-
-        close_on_exec(fd);
-        usb->fd = fd;
-
-        D("[ usb_thread - registering device ]");
-        register_usb_transport(usb, 0, 0, 1);
-    }
-
-    // never gets here
-    abort();
-}
-
-static int usb_adb_write(usb_handle *h, const void *data, int len)
-{
-    int n;
-
-    D("about to write (fd=%d, len=%d)", h->fd, len);
-    n = unix_write(h->fd, data, len);
-    if(n != len) {
-        D("ERROR: fd = %d, n = %d, errno = %d (%s)",
-            h->fd, n, errno, strerror(errno));
-        return -1;
-    }
-    if (h->kicked) {
-        D("usb_adb_write finished due to kicked");
-        return -1;
-    }
-    D("[ done fd=%d ]", h->fd);
-    return 0;
-}
-
-static int usb_adb_read(usb_handle *h, void *data, int len)
-{
-    D("about to read (fd=%d, len=%d)", h->fd, len);
-    while (len > 0) {
-        // The kernel implementation of adb_read in f_adb.c doesn't support
-        // reads larger then 4096 bytes. Read the data in 4096 byte chunks to
-        // avoid the issue. (The ffs implementation doesn't have this limit.)
-        int bytes_to_read = len < 4096 ? len : 4096;
-        int n = unix_read(h->fd, data, bytes_to_read);
-        if (n != bytes_to_read) {
-            D("ERROR: fd = %d, n = %d, errno = %d (%s)",
-                h->fd, n, errno, strerror(errno));
-            return -1;
-        }
-        if (h->kicked) {
-            D("usb_adb_read finished due to kicked");
-            return -1;
-        }
-        len -= n;
-        data = ((char*)data) + n;
-    }
-    D("[ done fd=%d ]", h->fd);
-    return 0;
-}
-
-static void usb_adb_kick(usb_handle *h) {
-    D("usb_kick");
-    // Other threads may be calling usb_adb_read/usb_adb_write at the same time.
-    // If we close h->fd, the file descriptor will be reused to open other files,
-    // and the read/write thread may operate on the wrong file. So instead
-    // we set the kicked flag and reopen h->fd to a dummy file here. After read/write
-    // threads finish, we close h->fd in usb_adb_close().
-    h->kicked = true;
-    TEMP_FAILURE_RETRY(dup2(dummy_fd, h->fd));
-}
-
-static void usb_adb_close(usb_handle *h) {
-    h->kicked = false;
-    adb_close(h->fd);
-    // Notify usb_adb_open_thread to open a new connection.
-    h->lock.lock();
-    h->open_new_connection = true;
-    h->lock.unlock();
-    h->notify.notify_one();
-}
-
-static void usb_adb_init()
-{
-    usb_handle* h = new usb_handle();
-
-    h->write = usb_adb_write;
-    h->read = usb_adb_read;
-    h->kick = usb_adb_kick;
-    h->close = usb_adb_close;
-
-    // Open the file /dev/android_adb_enable to trigger
-    // the enabling of the adb USB function in the kernel.
-    // We never touch this file again - just leave it open
-    // indefinitely so the kernel will know when we are running
-    // and when we are not.
-    int fd = unix_open("/dev/android_adb_enable", O_RDWR);
-    if (fd < 0) {
-       D("failed to open /dev/android_adb_enable");
-    } else {
-        close_on_exec(fd);
-    }
-
-    D("[ usb_init - starting thread ]");
-    if (!adb_thread_create(usb_adb_open_thread, h)) {
-        fatal_errno("cannot create usb thread");
-    }
-}
-
-
-static bool init_functionfs(struct usb_handle *h)
-{
+bool init_functionfs(struct usb_handle* h) {
     ssize_t ret;
     struct desc_v1 v1_descriptor;
     struct desc_v2 v2_descriptor;
+    size_t retries = 0;
 
     v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
     v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
@@ -444,6 +297,29 @@
         goto err;
     }
 
+    h->max_rw = MAX_PAYLOAD;
+    while (h->max_rw >= USB_FFS_BULK_SIZE && retries < ENDPOINT_ALLOC_RETRIES) {
+        int ret_in = ioctl(h->bulk_in, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(h->max_rw));
+        int errno_in = errno;
+        int ret_out = ioctl(h->bulk_out, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(h->max_rw));
+        int errno_out = errno;
+
+        if (ret_in || ret_out) {
+            if (errno_in == ENODEV || errno_out == ENODEV) {
+                std::this_thread::sleep_for(100ms);
+                retries += 1;
+                continue;
+            }
+            h->max_rw /= 2;
+        } else {
+            return true;
+        }
+    }
+
+    D("[ adb: cannot call endpoint alloc: errno=%d ]", errno);
+    // Kernel pre-allocation could have failed for recoverable reasons.
+    // Continue running with a safe max rw size.
+    h->max_rw = USB_FFS_BULK_SIZE;
     return true;
 
 err:
@@ -463,7 +339,7 @@
 }
 
 static void usb_ffs_open_thread(void* x) {
-    struct usb_handle *usb = (struct usb_handle *)x;
+    struct usb_handle* usb = (struct usb_handle*)x;
 
     adb_thread_setname("usb ffs open");
 
@@ -497,7 +373,7 @@
 
     const char* buf = static_cast<const char*>(data);
     while (len > 0) {
-        int write_len = std::min(USB_FFS_MAX_WRITE, len);
+        int write_len = std::min(h->max_rw, len);
         int n = adb_write(h->bulk_in, buf, write_len);
         if (n < 0) {
             D("ERROR: fd = %d, n = %d: %s", h->bulk_in, n, strerror(errno));
@@ -516,7 +392,7 @@
 
     char* buf = static_cast<char*>(data);
     while (len > 0) {
-        int read_len = std::min(USB_FFS_MAX_READ, len);
+        int read_len = std::min(h->max_rw, len);
         int n = adb_read(h->bulk_out, buf, read_len);
         if (n < 0) {
             D("ERROR: fd = %d, n = %d: %s", h->bulk_out, n, strerror(errno));
@@ -530,8 +406,7 @@
     return 0;
 }
 
-static void usb_ffs_kick(usb_handle *h)
-{
+static void usb_ffs_kick(usb_handle* h) {
     int err;
 
     err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT);
@@ -553,7 +428,7 @@
     TEMP_FAILURE_RETRY(dup2(dummy_fd, h->bulk_in));
 }
 
-static void usb_ffs_close(usb_handle *h) {
+static void usb_ffs_close(usb_handle* h) {
     h->kicked = false;
     adb_close(h->bulk_out);
     adb_close(h->bulk_in);
@@ -564,8 +439,7 @@
     h->notify.notify_one();
 }
 
-static void usb_ffs_init()
-{
+static void usb_ffs_init() {
     D("[ usb_init - using FunctionFS ]");
 
     usb_handle* h = new usb_handle();
@@ -581,33 +455,25 @@
     }
 }
 
-void usb_init()
-{
+void usb_init() {
     dummy_fd = adb_open("/dev/null", O_WRONLY);
     CHECK_NE(dummy_fd, -1);
-    if (access(USB_FFS_ADB_EP0, F_OK) == 0)
-        usb_ffs_init();
-    else
-        usb_adb_init();
+    usb_ffs_init();
 }
 
-int usb_write(usb_handle *h, const void *data, int len)
-{
+int usb_write(usb_handle* h, const void* data, int len) {
     return h->write(h, data, len);
 }
 
-int usb_read(usb_handle *h, void *data, int len)
-{
+int usb_read(usb_handle* h, void* data, int len) {
     return h->read(h, data, len);
 }
 
-int usb_close(usb_handle *h)
-{
+int usb_close(usb_handle* h) {
     h->close(h);
     return 0;
 }
 
-void usb_kick(usb_handle *h)
-{
+void usb_kick(usb_handle* h) {
     h->kick(h);
 }
diff --git a/adb/daemon/usb.h b/adb/daemon/usb.h
new file mode 100644
index 0000000..55b5995
--- /dev/null
+++ b/adb/daemon/usb.h
@@ -0,0 +1,45 @@
+#pragma once
+
+/*
+ * 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 <atomic>
+#include <condition_variable>
+#include <mutex>
+
+struct usb_handle {
+    usb_handle() : kicked(false) {
+    }
+
+    std::condition_variable notify;
+    std::mutex lock;
+    std::atomic<bool> kicked;
+    bool open_new_connection = true;
+
+    int (*write)(usb_handle* h, const void* data, int len);
+    int (*read)(usb_handle* h, void* data, int len);
+    void (*kick)(usb_handle* h);
+    void (*close)(usb_handle* h);
+
+    // FunctionFS
+    int control = -1;
+    int bulk_out = -1; /* "out" from the host's perspective => source for adbd */
+    int bulk_in = -1;  /* "in" from the host's perspective => sink for adbd */
+
+    int max_rw;
+};
+
+bool init_functionfs(struct usb_handle* h);
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 76119ef..22bd2f2 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -720,13 +720,7 @@
 }
 
 static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath,
-                      const char* name=nullptr) {
-    struct stat st;
-    if (!sync_stat_fallback(sc, rpath, &st)) {
-        sc.Error("stat failed when trying to receive %s: %s", rpath, strerror(errno));
-        return false;
-    }
-
+                      const char* name, uint64_t expected_size) {
     if (!sc.SendRequest(ID_RECV, rpath)) return false;
 
     adb_unlink(lpath);
@@ -778,7 +772,7 @@
         bytes_copied += msg.data.size;
 
         sc.RecordBytesTransferred(msg.data.size);
-        sc.ReportProgress(name != nullptr ? name : rpath, bytes_copied, st.st_size);
+        sc.ReportProgress(name != nullptr ? name : rpath, bytes_copied, expected_size);
     }
 
     sc.RecordFilesTransferred(1);
@@ -850,7 +844,8 @@
         // TODO(b/25566053): Make pushing empty directories work.
         // TODO(b/25457350): We don't preserve permissions on directories.
         sc.Warning("skipping empty directory '%s'", lpath.c_str());
-        copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(lpath), S_IFDIR);
+        copyinfo ci(android::base::Dirname(lpath), android::base::Dirname(rpath),
+                    android::base::Basename(lpath), S_IFDIR);
         ci.skip = true;
         file_list->push_back(ci);
         return true;
@@ -983,7 +978,7 @@
                 if (dst_dir.back() != '/') {
                     dst_dir.push_back('/');
                 }
-                dst_dir.append(adb_basename(src_path));
+                dst_dir.append(android::base::Basename(src_path));
             }
 
             success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), false, false);
@@ -1001,7 +996,7 @@
             if (path_holder.back() != '/') {
                 path_holder.push_back('/');
             }
-            path_holder += adb_basename(src_path);
+            path_holder += android::base::Basename(src_path);
             dst_path = path_holder.c_str();
         }
 
@@ -1021,7 +1016,8 @@
     std::vector<copyinfo> linklist;
 
     // Add an entry for the current directory to ensure it gets created before pulling its contents.
-    copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(lpath), S_IFDIR);
+    copyinfo ci(android::base::Dirname(lpath), android::base::Dirname(rpath),
+                android::base::Basename(lpath), S_IFDIR);
     file_list->push_back(ci);
 
     // Put the files/dirs in rpath on the lists.
@@ -1121,7 +1117,7 @@
                 continue;
             }
 
-            if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str())) {
+            if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str(), nullptr, ci.size)) {
                 return false;
             }
 
@@ -1155,7 +1151,7 @@
         if (srcs.size() == 1 && errno == ENOENT) {
             // However, its parent must exist.
             struct stat parent_st;
-            if (stat(adb_dirname(dst).c_str(), &parent_st) == -1) {
+            if (stat(android::base::Dirname(dst).c_str(), &parent_st) == -1) {
                 sc.Error("cannot create file/directory '%s': %s", dst, strerror(errno));
                 return false;
             }
@@ -1210,7 +1206,7 @@
                 if (!adb_is_separator(dst_dir.back())) {
                     dst_dir.push_back(OS_PATH_SEPARATOR);
                 }
-                dst_dir.append(adb_basename(src_path));
+                dst_dir.append(android::base::Basename(src_path));
             }
 
             success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(), copy_attrs);
@@ -1226,13 +1222,13 @@
             // really want to copy to local_dir + OS_PATH_SEPARATOR +
             // basename(remote).
             path_holder = android::base::StringPrintf("%s%c%s", dst_path, OS_PATH_SEPARATOR,
-                                                      adb_basename(src_path).c_str());
+                                                      android::base::Basename(src_path).c_str());
             dst_path = path_holder.c_str();
         }
 
         sc.NewTransfer();
         sc.SetExpectedTotalBytes(src_st.st_size);
-        if (!sync_recv(sc, src_path, dst_path, name)) {
+        if (!sync_recv(sc, src_path, dst_path, name, src_st.st_size)) {
             success = false;
             continue;
         }
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 43c877e..2acf661 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -31,6 +31,7 @@
 #include <unistd.h>
 #include <utime.h>
 
+#include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <private/android_filesystem_config.h>
@@ -39,10 +40,13 @@
 
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_trace.h"
 #include "adb_utils.h"
 #include "security_log_tags.h"
 #include "sysdeps/errno.h"
 
+using android::base::StringPrintf;
+
 static bool should_use_fs_config(const std::string& path) {
     // TODO: use fs_config to configure permissions on /data.
     return android::base::StartsWith(path, "/system/") ||
@@ -152,7 +156,7 @@
     if (!d) goto done;
 
     while ((de = readdir(d.get()))) {
-        std::string filename(android::base::StringPrintf("%s/%s", path, de->d_name));
+        std::string filename(StringPrintf("%s/%s", path, de->d_name));
 
         struct stat st;
         if (lstat(filename.c_str(), &st) == 0) {
@@ -191,7 +195,7 @@
 }
 
 static bool SendSyncFailErrno(int fd, const std::string& reason) {
-    return SendSyncFail(fd, android::base::StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
+    return SendSyncFail(fd, StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
 }
 
 static bool handle_send_file(int s, const char* path, uid_t uid, gid_t gid, uint64_t capabilities,
@@ -203,7 +207,7 @@
 
     int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
     if (fd < 0 && errno == ENOENT) {
-        if (!secure_mkdirs(adb_dirname(path))) {
+        if (!secure_mkdirs(android::base::Dirname(path))) {
             SendSyncFailErrno(s, "secure_mkdirs failed");
             goto fail;
         }
@@ -330,7 +334,7 @@
 
     ret = symlink(&buffer[0], path.c_str());
     if (ret && errno == ENOENT) {
-        if (!secure_mkdirs(adb_dirname(path))) {
+        if (!secure_mkdirs(android::base::Dirname(path))) {
             SendSyncFailErrno(s, "secure_mkdirs failed");
             return false;
         }
@@ -433,9 +437,31 @@
     return WriteFdExactly(s, &msg.data, sizeof(msg.data));
 }
 
+static const char* sync_id_to_name(uint32_t id) {
+  switch (id) {
+    case ID_LSTAT_V1:
+      return "lstat_v1";
+    case ID_LSTAT_V2:
+      return "lstat_v2";
+    case ID_STAT_V2:
+      return "stat_v2";
+    case ID_LIST:
+      return "list";
+    case ID_SEND:
+      return "send";
+    case ID_RECV:
+      return "recv";
+    case ID_QUIT:
+        return "quit";
+    default:
+        return "???";
+  }
+}
+
 static bool handle_sync_command(int fd, std::vector<char>& buffer) {
     D("sync: waiting for request");
 
+    ATRACE_CALL();
     SyncRequest request;
     if (!ReadFdExactly(fd, &request, sizeof(request))) {
         SendSyncFail(fd, "command read failure");
@@ -453,9 +479,11 @@
     }
     name[path_length] = 0;
 
-    const char* id = reinterpret_cast<const char*>(&request.id);
-    D("sync: '%.4s' '%s'", id, name);
+    std::string id_name = sync_id_to_name(request.id);
+    std::string trace_name = StringPrintf("%s(%s)", id_name.c_str(), name);
+    ATRACE_NAME(trace_name.c_str());
 
+    D("sync: %s('%s')", id_name.c_str(), name);
     switch (request.id) {
         case ID_LSTAT_V1:
             if (!do_lstat_v1(fd, name)) return false;
@@ -476,8 +504,7 @@
         case ID_QUIT:
             return false;
         default:
-            SendSyncFail(
-                fd, android::base::StringPrintf("unknown command '%.4s' (%08x)", id, request.id));
+            SendSyncFail(fd, StringPrintf("unknown command %08x", request.id));
             return false;
     }
 
diff --git a/adb/services.cpp b/adb/services.cpp
index 2fbc15a..a48d855 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -39,6 +39,7 @@
 
 #if !ADB_HOST
 #include <android-base/properties.h>
+#include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
 #include <private/android_logger.h>
 #endif
@@ -133,17 +134,12 @@
             return false;
         }
 
-        const char* const recovery_dir = "/cache/recovery";
-        const char* const command_file = "/cache/recovery/command";
-        // Ensure /cache/recovery exists.
-        if (adb_mkdir(recovery_dir, 0770) == -1 && errno != EEXIST) {
-            D("Failed to create directory '%s': %s", recovery_dir, strerror(errno));
-            return false;
-        }
-
-        bool write_status = android::base::WriteStringToFile(
-                auto_reboot ? "--sideload_auto_reboot" : "--sideload", command_file);
-        if (!write_status) {
+        const std::vector<std::string> options = {
+            auto_reboot ? "--sideload_auto_reboot" : "--sideload"
+        };
+        std::string err;
+        if (!write_bootloader_message(options, &err)) {
+            D("Failed to set bootloader message: %s", err.c_str());
             return false;
         }
 
@@ -246,6 +242,15 @@
     }
     D("socketpair: (%d,%d)", s[0], s[1]);
 
+#if !ADB_HOST
+    if (func == &file_sync_service) {
+        // Set file sync service socket to maximum size
+        int max_buf = LINUX_MAX_SOCKET_SIZE;
+        adb_setsockopt(s[0], SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
+        adb_setsockopt(s[1], SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
+    }
+#endif // !ADB_HOST
+
     stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
     if (sti == nullptr) {
         fatal("cannot allocate stinfo");
diff --git a/adb/set_verity_enable_state_service.cpp b/adb/set_verity_enable_state_service.cpp
index f9e028b..76b156d 100644
--- a/adb/set_verity_enable_state_service.cpp
+++ b/adb/set_verity_enable_state_service.cpp
@@ -108,11 +108,10 @@
         return;
     }
 
-    std::string fstab_filename = "/fstab." + android::base::GetProperty("ro.hardware", "");
-
-    fstab = fs_mgr_read_fstab(fstab_filename.c_str());
+    // read all fstab entries at once from all sources
+    fstab = fs_mgr_read_fstab_default();
     if (!fstab) {
-        WriteFdFmt(fd, "Failed to open %s\nMaybe run adb root?\n", fstab_filename.c_str());
+        WriteFdFmt(fd, "Failed to read fstab\nMaybe run adb root?\n");
         return;
     }
 
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index 4975fab..d4f334b 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -320,6 +320,10 @@
         parent_error_sfd.reset(-1);
         close_on_exec(child_error_sfd);
 
+        // adbd sets SIGPIPE to SIG_IGN to get EPIPE instead, and Linux propagates that to child
+        // processes, so we need to manually reset back to SIG_DFL here (http://b/35209888).
+        signal(SIGPIPE, SIG_DFL);
+
         if (command_.empty()) {
             execle(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr, cenv.data());
         } else {
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
old mode 100755
new mode 100644
diff --git a/adb/trace.sh b/adb/trace.sh
new file mode 100755
index 0000000..49e5026
--- /dev/null
+++ b/adb/trace.sh
@@ -0,0 +1,17 @@
+set -e
+
+if ! [ -e $ANDROID_BUILD_TOP/external/chromium-trace/systrace.py ]; then
+    echo "error: can't find systrace.py at \$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py"
+    exit 1
+fi
+
+adb shell "sleep 1; atrace -b 65536 --async_start adb sched power freq idle disk mmc load"
+adb shell killall adbd
+adb wait-for-device
+echo "press enter to finish..."
+read
+TRACE_TEMP=`mktemp /tmp/trace.XXXXXX`
+echo Saving trace to ${TRACE_TEMP}, html file to ${TRACE_TEMP}.html
+adb shell atrace --async_stop -z > ${TRACE_TEMP}
+$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py --from-file=${TRACE_TEMP} -o ${TRACE_TEMP}.html
+chrome ${TRACE_TEMP}.html
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 7b82b19..c951f5b 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -37,6 +37,7 @@
 
 #include "adb.h"
 #include "adb_auth.h"
+#include "adb_trace.h"
 #include "adb_utils.h"
 #include "diagnose_usb.h"
 
@@ -50,18 +51,18 @@
 const char* const kFeatureShell2 = "shell_v2";
 const char* const kFeatureCmd = "cmd";
 const char* const kFeatureStat2 = "stat_v2";
+const char* const kFeatureLibusb = "libusb";
 
 static std::string dump_packet(const char* name, const char* func, apacket* p) {
-    unsigned  command = p->msg.command;
-    int       len     = p->msg.data_length;
-    char      cmd[9];
-    char      arg0[12], arg1[12];
-    int       n;
+    unsigned command = p->msg.command;
+    int len = p->msg.data_length;
+    char cmd[9];
+    char arg0[12], arg1[12];
+    int n;
 
     for (n = 0; n < 4; n++) {
-        int  b = (command >> (n*8)) & 255;
-        if (b < 32 || b >= 127)
-            break;
+        int b = (command >> (n * 8)) & 255;
+        if (b < 32 || b >= 127) break;
         cmd[n] = (char)b;
     }
     if (n == 4) {
@@ -82,25 +83,24 @@
     else
         snprintf(arg1, sizeof arg1, "0x%x", p->msg.arg1);
 
-    std::string result = android::base::StringPrintf("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ",
-                                                     name, func, cmd, arg0, arg1, len);
+    std::string result = android::base::StringPrintf("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ", name,
+                                                     func, cmd, arg0, arg1, len);
     result += dump_hex(p->data, len);
     return result;
 }
 
-static int
-read_packet(int  fd, const char* name, apacket** ppacket)
-{
+static int read_packet(int fd, const char* name, apacket** ppacket) {
+    ATRACE_NAME("read_packet");
     char buff[8];
     if (!name) {
         snprintf(buff, sizeof buff, "fd=%d", fd);
         name = buff;
     }
-    char* p = reinterpret_cast<char*>(ppacket);  /* really read a packet address */
+    char* p = reinterpret_cast<char*>(ppacket); /* really read a packet address */
     int len = sizeof(apacket*);
-    while(len > 0) {
+    while (len > 0) {
         int r = adb_read(fd, p, len);
-        if(r > 0) {
+        if (r > 0) {
             len -= r;
             p += r;
         } else {
@@ -113,20 +113,19 @@
     return 0;
 }
 
-static int
-write_packet(int  fd, const char* name, apacket** ppacket)
-{
+static int write_packet(int fd, const char* name, apacket** ppacket) {
+    ATRACE_NAME("write_packet");
     char buff[8];
     if (!name) {
         snprintf(buff, sizeof buff, "fd=%d", fd);
         name = buff;
     }
     VLOG(TRANSPORT) << dump_packet(name, "to remote", *ppacket);
-    char* p = reinterpret_cast<char*>(ppacket);  /* we really write the packet address */
+    char* p = reinterpret_cast<char*>(ppacket); /* we really write the packet address */
     int len = sizeof(apacket*);
-    while(len > 0) {
+    while (len > 0) {
         int r = adb_write(fd, p, len);
-        if(r > 0) {
+        if (r > 0) {
             len -= r;
             p += r;
         } else {
@@ -137,16 +136,15 @@
     return 0;
 }
 
-static void transport_socket_events(int fd, unsigned events, void *_t)
-{
-    atransport *t = reinterpret_cast<atransport*>(_t);
+static void transport_socket_events(int fd, unsigned events, void* _t) {
+    atransport* t = reinterpret_cast<atransport*>(_t);
     D("transport_socket_events(fd=%d, events=%04x,...)", fd, events);
-    if(events & FDE_READ){
-        apacket *p = 0;
-        if(read_packet(fd, t->serial, &p)){
+    if (events & FDE_READ) {
+        apacket* p = 0;
+        if (read_packet(fd, t->serial, &p)) {
             D("%s: failed to read packet from transport socket on fd %d", t->serial, fd);
         } else {
-            handle_packet(p, (atransport *) _t);
+            handle_packet(p, (atransport*)_t);
         }
     }
 }
@@ -180,40 +178,43 @@
 // read_transport thread reads data from a transport (representing a usb/tcp connection),
 // and makes the main thread call handle_packet().
 static void read_transport_thread(void* _t) {
-    atransport *t = reinterpret_cast<atransport*>(_t);
-    apacket *p;
+    atransport* t = reinterpret_cast<atransport*>(_t);
+    apacket* p;
 
-    adb_thread_setname(android::base::StringPrintf("<-%s",
-                                                   (t->serial != nullptr ? t->serial : "transport")));
-    D("%s: starting read_transport thread on fd %d, SYNC online (%d)",
-       t->serial, t->fd, t->sync_token + 1);
+    adb_thread_setname(
+        android::base::StringPrintf("<-%s", (t->serial != nullptr ? t->serial : "transport")));
+    D("%s: starting read_transport thread on fd %d, SYNC online (%d)", t->serial, t->fd,
+      t->sync_token + 1);
     p = get_apacket();
     p->msg.command = A_SYNC;
     p->msg.arg0 = 1;
     p->msg.arg1 = ++(t->sync_token);
     p->msg.magic = A_SYNC ^ 0xffffffff;
-    if(write_packet(t->fd, t->serial, &p)) {
+    if (write_packet(t->fd, t->serial, &p)) {
         put_apacket(p);
         D("%s: failed to write SYNC packet", t->serial);
         goto oops;
     }
 
     D("%s: data pump started", t->serial);
-    for(;;) {
+    for (;;) {
+        ATRACE_NAME("read_transport loop");
         p = get_apacket();
 
-        if(t->read_from_remote(p, t) == 0){
-            D("%s: received remote packet, sending to transport",
-              t->serial);
-            if(write_packet(t->fd, t->serial, &p)){
+        {
+            ATRACE_NAME("read_transport read_remote");
+            if (t->read_from_remote(p, t) != 0) {
+                D("%s: remote read failed for transport", t->serial);
                 put_apacket(p);
-                D("%s: failed to write apacket to transport", t->serial);
-                goto oops;
+                break;
             }
-        } else {
-            D("%s: remote read failed for transport", t->serial);
+        }
+
+        D("%s: received remote packet, sending to transport", t->serial);
+        if (write_packet(t->fd, t->serial, &p)) {
             put_apacket(p);
-            break;
+            D("%s: failed to write apacket to transport", t->serial);
+            goto oops;
         }
     }
 
@@ -223,7 +224,7 @@
     p->msg.arg0 = 0;
     p->msg.arg1 = 0;
     p->msg.magic = A_SYNC ^ 0xffffffff;
-    if(write_packet(t->fd, t->serial, &p)) {
+    if (write_packet(t->fd, t->serial, &p)) {
         put_apacket(p);
         D("%s: failed to write SYNC apacket to transport", t->serial);
     }
@@ -237,38 +238,38 @@
 // write_transport thread gets packets sent by the main thread (through send_packet()),
 // and writes to a transport (representing a usb/tcp connection).
 static void write_transport_thread(void* _t) {
-    atransport *t = reinterpret_cast<atransport*>(_t);
-    apacket *p;
+    atransport* t = reinterpret_cast<atransport*>(_t);
+    apacket* p;
     int active = 0;
 
-    adb_thread_setname(android::base::StringPrintf("->%s",
-                                                   (t->serial != nullptr ? t->serial : "transport")));
-    D("%s: starting write_transport thread, reading from fd %d",
-       t->serial, t->fd);
+    adb_thread_setname(
+        android::base::StringPrintf("->%s", (t->serial != nullptr ? t->serial : "transport")));
+    D("%s: starting write_transport thread, reading from fd %d", t->serial, t->fd);
 
-    for(;;){
-        if(read_packet(t->fd, t->serial, &p)) {
-            D("%s: failed to read apacket from transport on fd %d",
-               t->serial, t->fd );
+    for (;;) {
+        ATRACE_NAME("write_transport loop");
+        if (read_packet(t->fd, t->serial, &p)) {
+            D("%s: failed to read apacket from transport on fd %d", t->serial, t->fd);
             break;
         }
-        if(p->msg.command == A_SYNC){
-            if(p->msg.arg0 == 0) {
+
+        if (p->msg.command == A_SYNC) {
+            if (p->msg.arg0 == 0) {
                 D("%s: transport SYNC offline", t->serial);
                 put_apacket(p);
                 break;
             } else {
-                if(p->msg.arg1 == t->sync_token) {
+                if (p->msg.arg1 == t->sync_token) {
                     D("%s: transport SYNC online", t->serial);
                     active = 1;
                 } else {
-                    D("%s: transport ignoring SYNC %d != %d",
-                      t->serial, p->msg.arg1, t->sync_token);
+                    D("%s: transport ignoring SYNC %d != %d", t->serial, p->msg.arg1, t->sync_token);
                 }
             }
         } else {
-            if(active) {
+            if (active) {
                 D("%s: transport got packet, sending to remote", t->serial);
+                ATRACE_NAME("write_transport write_remote");
                 t->write_to_remote(p, t);
             } else {
                 D("%s: transport ignoring packet while offline", t->serial);
@@ -296,7 +297,6 @@
 static int transport_registration_recv = -1;
 static fdevent transport_registration_fde;
 
-
 #if ADB_HOST
 
 /* this adds support required by the 'track-devices' service.
@@ -305,19 +305,17 @@
  * live TCP connection
  */
 struct device_tracker {
-    asocket          socket;
-    int              update_needed;
-    device_tracker*  next;
+    asocket socket;
+    int update_needed;
+    device_tracker* next;
 };
 
 /* linked list of all device trackers */
-static device_tracker*   device_tracker_list;
+static device_tracker* device_tracker_list;
 
-static void
-device_tracker_remove( device_tracker*  tracker )
-{
-    device_tracker**  pnode = &device_tracker_list;
-    device_tracker*   node  = *pnode;
+static void device_tracker_remove(device_tracker* tracker) {
+    device_tracker** pnode = &device_tracker_list;
+    device_tracker* node = *pnode;
 
     std::lock_guard<std::mutex> lock(transport_lock);
     while (node) {
@@ -326,17 +324,15 @@
             break;
         }
         pnode = &node->next;
-        node  = *pnode;
+        node = *pnode;
     }
 }
 
-static void
-device_tracker_close( asocket*  socket )
-{
-    device_tracker*  tracker = (device_tracker*) socket;
-    asocket*         peer    = socket->peer;
+static void device_tracker_close(asocket* socket) {
+    device_tracker* tracker = (device_tracker*)socket;
+    asocket* peer = socket->peer;
 
-    D( "device tracker %p removed", tracker);
+    D("device tracker %p removed", tracker);
     if (peer) {
         peer->peer = NULL;
         peer->close(peer);
@@ -345,9 +341,7 @@
     free(tracker);
 }
 
-static int
-device_tracker_enqueue( asocket*  socket, apacket*  p )
-{
+static int device_tracker_enqueue(asocket* socket, apacket* p) {
     /* you can't read from a device tracker, close immediately */
     put_apacket(p);
     device_tracker_close(socket);
@@ -377,26 +371,23 @@
     }
 }
 
-asocket*
-create_device_tracker(void)
-{
+asocket* create_device_tracker(void) {
     device_tracker* tracker = reinterpret_cast<device_tracker*>(calloc(1, sizeof(*tracker)));
     if (tracker == nullptr) fatal("cannot allocate device tracker");
 
-    D( "device tracker %p created", tracker);
+    D("device tracker %p created", tracker);
 
     tracker->socket.enqueue = device_tracker_enqueue;
-    tracker->socket.ready   = device_tracker_ready;
-    tracker->socket.close   = device_tracker_close;
-    tracker->update_needed  = 1;
+    tracker->socket.ready = device_tracker_ready;
+    tracker->socket.close = device_tracker_close;
+    tracker->update_needed = 1;
 
-    tracker->next       = device_tracker_list;
+    tracker->next = device_tracker_list;
     device_tracker_list = tracker;
 
     return &tracker->socket;
 }
 
-
 // Call this function each time the transport list has changed.
 void update_transports() {
     std::string transports = list_transports(false);
@@ -416,26 +407,23 @@
     // Nothing to do on the device side.
 }
 
-#endif // ADB_HOST
+#endif  // ADB_HOST
 
-struct tmsg
-{
-    atransport *transport;
-    int         action;
+struct tmsg {
+    atransport* transport;
+    int action;
 };
 
-static int
-transport_read_action(int  fd, struct tmsg*  m)
-{
-    char *p   = (char*)m;
-    int   len = sizeof(*m);
-    int   r;
+static int transport_read_action(int fd, struct tmsg* m) {
+    char* p = (char*)m;
+    int len = sizeof(*m);
+    int r;
 
-    while(len > 0) {
+    while (len > 0) {
         r = adb_read(fd, p, len);
-        if(r > 0) {
+        if (r > 0) {
             len -= r;
-            p   += r;
+            p += r;
         } else {
             D("transport_read_action: on fd %d: %s", fd, strerror(errno));
             return -1;
@@ -444,18 +432,16 @@
     return 0;
 }
 
-static int
-transport_write_action(int  fd, struct tmsg*  m)
-{
-    char *p   = (char*)m;
-    int   len = sizeof(*m);
-    int   r;
+static int transport_write_action(int fd, struct tmsg* m) {
+    char* p = (char*)m;
+    int len = sizeof(*m);
+    int r;
 
-    while(len > 0) {
+    while (len > 0) {
         r = adb_write(fd, p, len);
-        if(r > 0) {
+        if (r > 0) {
             len -= r;
-            p   += r;
+            p += r;
         } else {
             D("transport_write_action: on fd %d: %s", fd, strerror(errno));
             return -1;
@@ -464,17 +450,16 @@
     return 0;
 }
 
-static void transport_registration_func(int _fd, unsigned ev, void *data)
-{
+static void transport_registration_func(int _fd, unsigned ev, void* data) {
     tmsg m;
     int s[2];
-    atransport *t;
+    atransport* t;
 
-    if(!(ev & FDE_READ)) {
+    if (!(ev & FDE_READ)) {
         return;
     }
 
-    if(transport_read_action(_fd, &m)) {
+    if (transport_read_action(_fd, &m)) {
         fatal_errno("cannot read transport registration socket");
     }
 
@@ -483,9 +468,9 @@
     if (m.action == 0) {
         D("transport: %s removing and free'ing %d", t->serial, t->transport_socket);
 
-            /* IMPORTANT: the remove closes one half of the
-            ** socket pair.  The close closes the other half.
-            */
+        /* IMPORTANT: the remove closes one half of the
+        ** socket pair.  The close closes the other half.
+        */
         fdevent_remove(&(t->transport_fde));
         adb_close(t->fd);
 
@@ -494,16 +479,11 @@
             transport_list.remove(t);
         }
 
-        if (t->product)
-            free(t->product);
-        if (t->serial)
-            free(t->serial);
-        if (t->model)
-            free(t->model);
-        if (t->device)
-            free(t->device);
-        if (t->devpath)
-            free(t->devpath);
+        if (t->product) free(t->product);
+        if (t->serial) free(t->serial);
+        if (t->model) free(t->model);
+        if (t->device) free(t->device);
+        if (t->devpath) free(t->devpath);
 
         delete t;
 
@@ -525,10 +505,7 @@
         t->transport_socket = s[0];
         t->fd = s[1];
 
-        fdevent_install(&(t->transport_fde),
-                        t->transport_socket,
-                        transport_socket_events,
-                        t);
+        fdevent_install(&(t->transport_fde), t->transport_socket, transport_socket_events, t);
 
         fdevent_set(&(t->transport_fde), FDE_READ);
 
@@ -550,11 +527,10 @@
     update_transports();
 }
 
-void init_transport_registration(void)
-{
+void init_transport_registration(void) {
     int s[2];
 
-    if(adb_socketpair(s)){
+    if (adb_socketpair(s)) {
         fatal_errno("cannot open transport registration socketpair");
     }
     D("socketpair: (%d,%d)", s[0], s[1]);
@@ -562,38 +538,33 @@
     transport_registration_send = s[0];
     transport_registration_recv = s[1];
 
-    fdevent_install(&transport_registration_fde,
-                    transport_registration_recv,
-                    transport_registration_func,
-                    0);
+    fdevent_install(&transport_registration_fde, transport_registration_recv,
+                    transport_registration_func, 0);
 
     fdevent_set(&transport_registration_fde, FDE_READ);
 }
 
 /* the fdevent select pump is single threaded */
-static void register_transport(atransport *transport)
-{
+static void register_transport(atransport* transport) {
     tmsg m;
     m.transport = transport;
     m.action = 1;
     D("transport: %s registered", transport->serial);
-    if(transport_write_action(transport_registration_send, &m)) {
+    if (transport_write_action(transport_registration_send, &m)) {
         fatal_errno("cannot write transport registration socket\n");
     }
 }
 
-static void remove_transport(atransport *transport)
-{
+static void remove_transport(atransport* transport) {
     tmsg m;
     m.transport = transport;
     m.action = 0;
     D("transport: %s removed", transport->serial);
-    if(transport_write_action(transport_registration_send, &m)) {
+    if (transport_write_action(transport_registration_send, &m)) {
         fatal_errno("cannot write transport registration socket\n");
     }
 }
 
-
 static void transport_unref(atransport* t) {
     CHECK(t != nullptr);
 
@@ -609,37 +580,31 @@
     }
 }
 
-static int qual_match(const char *to_test,
-                      const char *prefix, const char *qual, bool sanitize_qual)
-{
-    if (!to_test || !*to_test)
-        /* Return true if both the qual and to_test are null strings. */
+static int qual_match(const char* to_test, const char* prefix, const char* qual,
+                      bool sanitize_qual) {
+    if (!to_test || !*to_test) /* Return true if both the qual and to_test are null strings. */
         return !qual || !*qual;
 
-    if (!qual)
-        return 0;
+    if (!qual) return 0;
 
     if (prefix) {
         while (*prefix) {
-            if (*prefix++ != *to_test++)
-                return 0;
+            if (*prefix++ != *to_test++) return 0;
         }
     }
 
     while (*qual) {
         char ch = *qual++;
-        if (sanitize_qual && !isalnum(ch))
-            ch = '_';
-        if (ch != *to_test++)
-            return 0;
+        if (sanitize_qual && !isalnum(ch)) ch = '_';
+        if (ch != *to_test++) return 0;
     }
 
     /* Everything matched so far.  Return true if *to_test is a NUL. */
     return !*to_test;
 }
 
-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) {
     atransport* result = nullptr;
 
     if (serial) {
@@ -737,15 +702,24 @@
 
 const std::string atransport::connection_state_name() const {
     switch (connection_state) {
-        case kCsOffline: return "offline";
-        case kCsBootloader: return "bootloader";
-        case kCsDevice: return "device";
-        case kCsHost: return "host";
-        case kCsRecovery: return "recovery";
-        case kCsNoPerm: return UsbNoPermissionsShortHelpText();
-        case kCsSideload: return "sideload";
-        case kCsUnauthorized: return "unauthorized";
-        default: return "unknown";
+        case kCsOffline:
+            return "offline";
+        case kCsBootloader:
+            return "bootloader";
+        case kCsDevice:
+            return "device";
+        case kCsHost:
+            return "host";
+        case kCsRecovery:
+            return "recovery";
+        case kCsNoPerm:
+            return UsbNoPermissionsShortHelpText();
+        case kCsSideload:
+            return "sideload";
+        case kCsUnauthorized:
+            return "unauthorized";
+        default:
+            return "unknown";
     }
 }
 
@@ -771,9 +745,7 @@
 const FeatureSet& supported_features() {
     // Local static allocation to avoid global non-POD variables.
     static const FeatureSet* features = new FeatureSet{
-        kFeatureShell2,
-        kFeatureCmd,
-        kFeatureStat2,
+        kFeatureShell2, kFeatureCmd, kFeatureStat2,
         // Increment ADB_SERVER_VERSION whenever the feature list changes to
         // make sure that the adb client and server features stay in sync
         // (http://b/24370690).
@@ -791,14 +763,12 @@
         return FeatureSet();
     }
 
-    auto names = android::base::Split(features_string,
-                                      {kFeatureStringDelimiter});
+    auto names = android::base::Split(features_string, {kFeatureStringDelimiter});
     return FeatureSet(names.begin(), names.end());
 }
 
 bool CanUseFeature(const FeatureSet& feature_set, const std::string& feature) {
-    return feature_set.count(feature) > 0 &&
-            supported_features().count(feature) > 0;
+    return feature_set.count(feature) > 0 && supported_features().count(feature) > 0;
 }
 
 bool atransport::has_feature(const std::string& feature) const {
@@ -834,21 +804,20 @@
 
             // For fastboot compatibility, ignore protocol prefixes.
             if (android::base::StartsWith(target, "tcp:") ||
-                    android::base::StartsWith(target, "udp:")) {
+                android::base::StartsWith(target, "udp:")) {
                 local_target_ptr += 4;
             }
 
             // Parse our |serial| and the given |target| to check if the hostnames and ports match.
             std::string serial_host, error;
             int serial_port = -1;
-            if (android::base::ParseNetAddress(serial, &serial_host, &serial_port, nullptr,
-                                               &error)) {
+            if (android::base::ParseNetAddress(serial, &serial_host, &serial_port, nullptr, &error)) {
                 // |target| may omit the port to default to ours.
                 std::string target_host;
                 int target_port = serial_port;
                 if (android::base::ParseNetAddress(local_target_ptr, &target_host, &target_port,
                                                    nullptr, &error) &&
-                        serial_host == target_host && serial_port == target_port) {
+                    serial_host == target_host && serial_port == target_port) {
                     return true;
                 }
             }
@@ -863,8 +832,8 @@
 
 #if ADB_HOST
 
-static void append_transport_info(std::string* result, const char* key,
-                                  const char* value, bool sanitize) {
+static void append_transport_info(std::string* result, const char* key, const char* value,
+                                  bool sanitize) {
     if (value == nullptr || *value == '\0') {
         return;
     }
@@ -877,8 +846,7 @@
     }
 }
 
-static void append_transport(const atransport* t, std::string* result,
-                             bool long_listing) {
+static void append_transport(const atransport* t, std::string* result, bool long_listing) {
     const char* serial = t->serial;
     if (!serial || !serial[0]) {
         serial = "(no serial number)";
@@ -889,8 +857,7 @@
         *result += '\t';
         *result += t->connection_state_name();
     } else {
-        android::base::StringAppendF(result, "%-22s %s", serial,
-                                     t->connection_state_name().c_str());
+        android::base::StringAppendF(result, "%-22s %s", serial, t->connection_state_name().c_str());
 
         append_transport_info(result, "", t->devpath, false);
         append_transport_info(result, "product:", t->product, false);
@@ -923,9 +890,9 @@
 void close_usb_devices() {
     close_usb_devices([](const atransport*) { return true; });
 }
-#endif // ADB_HOST
+#endif  // ADB_HOST
 
-int register_socket_transport(int s, const char *serial, int port, int local) {
+int register_socket_transport(int s, const char* serial, int port, int local) {
     atransport* t = new atransport();
 
     if (!serial) {
@@ -944,7 +911,7 @@
     for (const auto& transport : pending_list) {
         if (transport->serial && strcmp(serial, transport->serial) == 0) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
-                << " is already in pending_list and fails to register";
+                            << " is already in pending_list and fails to register";
             delete t;
             return -1;
         }
@@ -953,7 +920,7 @@
     for (const auto& transport : transport_list) {
         if (transport->serial && strcmp(serial, transport->serial) == 0) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
-                << " is already in transport_list and fails to register";
+                            << " is already in transport_list and fails to register";
             delete t;
             return -1;
         }
@@ -969,7 +936,7 @@
 }
 
 #if ADB_HOST
-atransport *find_transport(const char *serial) {
+atransport* find_transport(const char* serial) {
     atransport* result = nullptr;
 
     std::lock_guard<std::mutex> lock(transport_lock);
@@ -998,14 +965,13 @@
 
 #endif
 
-void register_usb_transport(usb_handle* usb, const char* serial,
-                            const char* devpath, unsigned writeable) {
+void register_usb_transport(usb_handle* usb, const char* serial, const char* devpath,
+                            unsigned writeable) {
     atransport* t = new atransport();
 
-    D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb,
-      serial ? serial : "");
+    D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb, serial ? serial : "");
     init_usb_transport(t, usb, (writeable ? kCsOffline : kCsNoPerm));
-    if(serial) {
+    if (serial) {
         t->serial = strdup(serial);
     }
 
@@ -1022,23 +988,21 @@
 }
 
 // This should only be used for transports with connection_state == kCsNoPerm.
-void unregister_usb_transport(usb_handle *usb) {
+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;
-    });
+    transport_list.remove_if(
+        [usb](atransport* t) { return t->usb == usb && t->connection_state == kCsNoPerm; });
 }
 
-int check_header(apacket *p, atransport *t)
-{
-    if(p->msg.magic != (p->msg.command ^ 0xffffffff)) {
+int check_header(apacket* p, atransport* t) {
+    if (p->msg.magic != (p->msg.command ^ 0xffffffff)) {
         VLOG(RWX) << "check_header(): invalid magic";
         return -1;
     }
 
-    if(p->msg.data_length > t->get_max_payload()) {
-        VLOG(RWX) << "check_header(): " << p->msg.data_length << " atransport::max_payload = "
-                  << t->get_max_payload();
+    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;
     }
 
diff --git a/adb/transport.h b/adb/transport.h
index 3306388..490e513 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -47,6 +47,8 @@
 // The 'cmd' command is available
 extern const char* const kFeatureCmd;
 extern const char* const kFeatureStat2;
+// The server is running with libusb enabled.
+extern const char* const kFeatureLibusb;
 
 class atransport {
 public:
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index d054601..516b4f2 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -93,9 +93,16 @@
     t->usb = h;
 }
 
-#if ADB_HOST
 int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol)
 {
     return (usb_class == ADB_CLASS && usb_subclass == ADB_SUBCLASS && usb_protocol == ADB_PROTOCOL);
 }
+
+bool should_use_libusb() {
+#if defined(_WIN32) || !ADB_HOST
+    return false;
+#else
+    static bool disable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "0") == 0;
+    return !disable;
 #endif
+}
diff --git a/adb/usb.h b/adb/usb.h
new file mode 100644
index 0000000..ba70de4
--- /dev/null
+++ b/adb/usb.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+// USB host/client interface.
+
+#define ADB_USB_INTERFACE(handle_ref_type)                       \
+    void usb_init();                                             \
+    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)
+
+#if defined(_WIN32) || !ADB_HOST
+// Windows and the daemon have a single implementation.
+
+struct usb_handle;
+ADB_USB_INTERFACE(usb_handle*);
+
+#else // linux host || darwin
+// Linux and Darwin clients have native and libusb implementations.
+
+namespace libusb {
+    struct usb_handle;
+    ADB_USB_INTERFACE(libusb::usb_handle*);
+}
+
+namespace native {
+    struct usb_handle;
+    ADB_USB_INTERFACE(native::usb_handle*);
+}
+
+// Empty base that both implementations' opaque handles inherit from.
+struct usb_handle {
+};
+
+ADB_USB_INTERFACE(::usb_handle*);
+
+#endif // linux host || darwin
+
+
+// USB device detection.
+int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol);
+
+bool should_use_libusb();
diff --git a/adf/libadf/Android.bp b/adf/libadf/Android.bp
index 2b5461e..c276c53 100644
--- a/adf/libadf/Android.bp
+++ b/adf/libadf/Android.bp
@@ -14,8 +14,10 @@
 
 cc_library_static {
     name: "libadf",
-    srcs: ["adf.c"],
+    srcs: ["adf.cpp"],
     cflags: ["-Werror"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
 }
+
+subdirs = ["tests"]
diff --git a/adf/libadf/adf.c b/adf/libadf/adf.cpp
similarity index 68%
rename from adf/libadf/adf.c
rename to adf/libadf/adf.cpp
index c4d6681..60d8ef0 100644
--- a/adf/libadf/adf.c
+++ b/adf/libadf/adf.cpp
@@ -23,6 +23,10 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <algorithm>
+#include <memory>
+#include <vector>
+
 #include <linux/limits.h>
 
 #include <sys/ioctl.h>
@@ -31,52 +35,44 @@
 
 #define ADF_BASE_PATH "/dev/"
 
-static ssize_t adf_find_nodes(const char *pattern, adf_id_t **ids)
+static ssize_t adf_id_vector_to_array(const std::vector<adf_id_t> &in,
+        adf_id_t **out)
 {
-    DIR *dir;
-    struct dirent *dirent;
-    size_t n = 0;
-    ssize_t ret;
-    adf_id_t *ids_ret = NULL;
+    auto size = sizeof(in[0]) * in.size();
+    // We can't use new[] since the existing API says the caller should free()
+    // the returned array
+    auto ret = static_cast<adf_id_t *>(malloc(size));
+    if (!ret)
+        return -ENOMEM;
 
-    dir = opendir(ADF_BASE_PATH);
+    std::copy(in.begin(), in.end(), ret);
+    *out = ret;
+    return in.size();
+}
+
+static ssize_t adf_find_nodes(const char *pattern, adf_id_t **ids_out)
+{
+    struct dirent *dirent;
+    std::unique_ptr<DIR, decltype(&closedir)>
+        dir{opendir(ADF_BASE_PATH), closedir};
     if (!dir)
         return -errno;
 
+    std::vector<adf_id_t> ids;
     errno = 0;
-    while ((dirent = readdir(dir))) {
+    while ((dirent = readdir(dir.get()))) {
         adf_id_t id;
         int matched = sscanf(dirent->d_name, pattern, &id);
 
-        if (matched < 0) {
-            ret = -errno;
-            goto done;
-        } else if (matched != 1) {
-            continue;
-        }
-
-        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
-        if (!new_ids) {
-            ret = -ENOMEM;
-            goto done;
-        }
-
-        ids_ret = new_ids;
-        ids_ret[n] = id;
-        n++;
+        if (matched < 0)
+            return -errno;
+        else if (matched == 1)
+            ids.push_back(id);
     }
     if (errno)
-        ret = -errno;
-    else
-        ret = n;
+        return -errno;
 
-done:
-    closedir(dir);
-    if (ret < 0)
-        free(ids_ret);
-    else
-        *ids = ids_ret;
-    return ret;
+    return adf_id_vector_to_array(ids, ids_out);
 }
 
 ssize_t adf_devices(adf_id_t **ids)
@@ -115,46 +111,29 @@
     if (err < 0)
         return -ENOMEM;
 
-    if (data->n_attachments) {
-        data->attachments = malloc(sizeof(data->attachments[0]) *
-                data->n_attachments);
-        if (!data->attachments)
-            return -ENOMEM;
-    }
+    if (data->n_attachments)
+        data->attachments = new adf_attachment_config[data->n_attachments];
 
-    if (data->n_allowed_attachments) {
+    if (data->n_allowed_attachments)
         data->allowed_attachments =
-                malloc(sizeof(data->allowed_attachments[0]) *
-                        data->n_allowed_attachments);
-        if (!data->allowed_attachments) {
-            ret = -ENOMEM;
-            goto done;
-        }
-    }
+                new adf_attachment_config[data->n_allowed_attachments];
 
-    if (data->custom_data_size) {
-        data->custom_data = malloc(data->custom_data_size);
-        if (!data->custom_data) {
-            ret = -ENOMEM;
-            goto done;
-        }
-    }
+    if (data->custom_data_size)
+        data->custom_data = new char[data->custom_data_size];
 
     err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data);
-    if (err < 0)
+    if (err < 0) {
         ret = -errno;
-
-done:
-    if (ret < 0)
         adf_free_device_data(data);
+    }
     return ret;
 }
 
 void adf_free_device_data(struct adf_device_data *data)
 {
-    free(data->attachments);
-    free(data->allowed_attachments);
-    free(data->custom_data);
+    delete [] data->attachments;
+    delete [] data->allowed_attachments;
+    delete [] static_cast<char *>(data->custom_data);
 }
 
 int adf_device_post(struct adf_device *dev,
@@ -180,6 +159,37 @@
     return (int)data.complete_fence;
 }
 
+int adf_device_post_v2(struct adf_device *dev,
+        adf_id_t *interfaces, __u32 n_interfaces,
+        struct adf_buffer_config *bufs, __u32 n_bufs,
+        void *custom_data, __u64 custom_data_size,
+        enum adf_complete_fence_type complete_fence_type,
+        int *complete_fence)
+{
+    int err;
+    struct adf_post_config_v2 data;
+
+    memset(&data, 0, sizeof(data));
+    data.interfaces = (uintptr_t)interfaces;
+    data.n_interfaces = n_interfaces;
+    data.bufs = (uintptr_t)bufs;
+    data.n_bufs = n_bufs;
+    data.custom_data = (uintptr_t)custom_data;
+    data.custom_data_size = custom_data_size;
+    data.complete_fence_type = complete_fence_type;
+
+    err = ioctl(dev->fd, ADF_POST_CONFIG_V2, &data);
+    if (err < 0)
+        return -errno;
+
+    if (complete_fence)
+        *complete_fence = data.complete_fence;
+    else if (data.complete_fence >= 0)
+        close(data.complete_fence);
+
+    return 0;
+}
+
 static int adf_device_attachment(struct adf_device *dev,
         adf_id_t overlay_engine, adf_id_t interface, bool attach)
 {
@@ -221,39 +231,17 @@
         adf_id_t overlay_engine, adf_id_t **interfaces)
 {
     struct adf_device_data data;
-    ssize_t n = 0;
-    ssize_t ret;
-    adf_id_t *ids_ret = NULL;
+    auto err = adf_get_device_data(dev, &data);
+    if (err < 0)
+        return err;
 
-    ret = adf_get_device_data(dev, &data);
-    if (ret < 0)
-        return ret;
+    std::vector<adf_id_t> ids;
+    for (size_t i = 0; i < data.n_allowed_attachments; i++)
+        if (data.allowed_attachments[i].overlay_engine == overlay_engine)
+            ids.push_back(data.allowed_attachments[i].interface);
 
-    size_t i;
-    for (i = 0; i < data.n_allowed_attachments; i++) {
-        if (data.allowed_attachments[i].overlay_engine != overlay_engine)
-            continue;
-
-        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
-        if (!new_ids) {
-            ret = -ENOMEM;
-            goto done;
-        }
-
-        ids_ret = new_ids;
-        ids_ret[n] = data.allowed_attachments[i].interface;
-        n++;
-    }
-
-    ret = n;
-
-done:
     adf_free_device_data(&data);
-    if (ret < 0)
-        free(ids_ret);
-    else
-        *interfaces = ids_ret;
-    return ret;
+    return adf_id_vector_to_array(ids, interfaces);
 }
 
 static ssize_t adf_interfaces_filter(struct adf_device *dev,
@@ -261,46 +249,23 @@
         bool (*filter)(struct adf_interface_data *data, __u32 match),
         __u32 match)
 {
-    size_t n = 0;
-    ssize_t ret;
-    adf_id_t *ids_ret = NULL;
-
-    size_t i;
-    for (i = 0; i < n_in; i++) {
+    std::vector<adf_id_t> ids;
+    for (size_t i = 0; i < n_in; i++) {
         int fd = adf_interface_open(dev, in[i], O_RDONLY);
-        if (fd < 0) {
-            ret = fd;
-            goto done;
-        }
+        if (fd < 0)
+            return fd;
 
         struct adf_interface_data data;
-        ret = adf_get_interface_data(fd, &data);
+        auto ret = adf_get_interface_data(fd, &data);
         close(fd);
         if (ret < 0)
-            goto done;
+            return ret;
 
-        if (!filter(&data, match))
-            continue;
-
-        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
-        if (!new_ids) {
-            ret = -ENOMEM;
-            goto done;
-        }
-
-        ids_ret = new_ids;
-        ids_ret[n] = in[i];
-        n++;
+        if (filter(&data, match))
+            ids.push_back(in[i]);
     }
 
-    ret = n;
-
-done:
-    if (ret < 0)
-        free(ids_ret);
-    else
-        *out = ids_ret;
-    return ret;
+    return adf_id_vector_to_array(ids, out);
 }
 
 static bool adf_interface_type_filter(struct adf_interface_data *data,
@@ -354,35 +319,24 @@
     if (err < 0)
         return -errno;
 
-    if (data->n_available_modes) {
-        data->available_modes = malloc(sizeof(data->available_modes[0]) *
-                data->n_available_modes);
-        if (!data->available_modes)
-            return -ENOMEM;
-    }
+    if (data->n_available_modes)
+        data->available_modes = new drm_mode_modeinfo[data->n_available_modes];
 
-    if (data->custom_data_size) {
-        data->custom_data = malloc(data->custom_data_size);
-        if (!data->custom_data) {
-            ret = -ENOMEM;
-            goto done;
-        }
-    }
+    if (data->custom_data_size)
+        data->custom_data = new char[data->custom_data_size];
 
     err = ioctl(fd, ADF_GET_INTERFACE_DATA, data);
-    if (err < 0)
+    if (err < 0) {
         ret = -errno;
-
-done:
-    if (ret < 0)
         adf_free_interface_data(data);
+    }
     return ret;
 }
 
 void adf_free_interface_data(struct adf_interface_data *data)
 {
-    free(data->available_modes);
-    free(data->custom_data);
+    delete [] data->available_modes;
+    delete [] static_cast<char *>(data->custom_data);
 }
 
 int adf_interface_blank(int fd, __u8 mode)
@@ -421,6 +375,21 @@
     return (int)data.fd;
 }
 
+static void adf_interface_simple_post_config_buf(struct adf_buffer_config *buf,
+        __u32 overlay_engine, __u32 w, __u32 h, __u32 format, int buf_fd,
+        __u32 offset, __u32 pitch, int acquire_fence)
+{
+    buf->overlay_engine = overlay_engine;
+    buf->w = w;
+    buf->h = h;
+    buf->format = format;
+    buf->fd[0] = buf_fd;
+    buf->offset[0] = offset;
+    buf->pitch[0] = pitch;
+    buf->n_planes = 1;
+    buf->acquire_fence = acquire_fence;
+}
+
 int adf_interface_simple_post(int fd, __u32 overlay_engine,
         __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
         __u32 pitch, int acquire_fence)
@@ -429,16 +398,8 @@
     struct adf_simple_post_config data;
 
     memset(&data, 0, sizeof(data));
-    data.buf.overlay_engine = overlay_engine;
-    data.buf.w = w;
-    data.buf.h = h;
-    data.buf.format = format;
-    data.buf.fd[0] = buf_fd;
-    data.buf.offset[0] = offset;
-    data.buf.pitch[0] = pitch;
-    data.buf.n_planes = 1;
-    data.buf.acquire_fence = acquire_fence;
-
+    adf_interface_simple_post_config_buf(&data.buf, overlay_engine, w, h, format,
+            buf_fd, offset, pitch, acquire_fence);
     ret = ioctl(fd, ADF_SIMPLE_POST_CONFIG, &data);
     if (ret < 0)
         return -errno;
@@ -446,6 +407,32 @@
     return (int)data.complete_fence;
 }
 
+int adf_interface_simple_post_v2(int fd, adf_id_t overlay_engine,
+        __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
+        __u32 pitch, int acquire_fence,
+        enum adf_complete_fence_type complete_fence_type,
+        int *complete_fence)
+{
+    int ret;
+    struct adf_simple_post_config_v2 data;
+
+    memset(&data, 0, sizeof(data));
+    adf_interface_simple_post_config_buf(&data.buf, overlay_engine, w, h, format,
+            buf_fd, offset, pitch, acquire_fence);
+    data.complete_fence_type = complete_fence_type;
+
+    ret = ioctl(fd, ADF_SIMPLE_POST_CONFIG_V2, &data);
+    if (ret < 0)
+        return -errno;
+
+    if (complete_fence)
+        *complete_fence = data.complete_fence;
+    else if (data.complete_fence >= 0)
+        close(data.complete_fence);
+
+    return 0;
+}
+
 ssize_t adf_overlay_engines(struct adf_device *dev, adf_id_t **overlay_engines)
 {
     char pattern[64];
@@ -458,39 +445,16 @@
         adf_id_t interface, adf_id_t **overlay_engines)
 {
     struct adf_device_data data;
-    ssize_t n = 0;
-    ssize_t ret;
-    adf_id_t *ids_ret = NULL;
+    auto err = adf_get_device_data(dev, &data);
+    if (err < 0)
+        return err;
 
-    ret = adf_get_device_data(dev, &data);
-    if (ret < 0)
-        return ret;
+    std::vector<adf_id_t> ids;
+    for (size_t i = 0; i < data.n_allowed_attachments; i++)
+        if (data.allowed_attachments[i].interface == interface)
+            ids.push_back(data.allowed_attachments[i].overlay_engine);
 
-    size_t i;
-    for (i = 0; i < data.n_allowed_attachments; i++) {
-        if (data.allowed_attachments[i].interface != interface)
-            continue;
-
-        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
-        if (!new_ids) {
-            ret = -ENOMEM;
-            goto done;
-        }
-
-        ids_ret = new_ids;
-        ids_ret[n] = data.allowed_attachments[i].overlay_engine;
-        n++;
-    }
-
-    ret = n;
-
-done:
-    adf_free_device_data(&data);
-    if (ret < 0)
-        free(ids_ret);
-    else
-        *overlay_engines = ids_ret;
-    return ret;
+    return adf_id_vector_to_array(ids, overlay_engines);
 }
 
 static ssize_t adf_overlay_engines_filter(struct adf_device *dev,
@@ -498,46 +462,24 @@
         bool (*filter)(struct adf_overlay_engine_data *data, void *cookie),
         void *cookie)
 {
-    size_t n = 0;
-    ssize_t ret;
-    adf_id_t *ids_ret = NULL;
-
+    std::vector<adf_id_t> ids;
     size_t i;
     for (i = 0; i < n_in; i++) {
         int fd = adf_overlay_engine_open(dev, in[i], O_RDONLY);
-        if (fd < 0) {
-            ret = fd;
-            goto done;
-        }
+        if (fd < 0)
+            return fd;
 
         struct adf_overlay_engine_data data;
-        ret = adf_get_overlay_engine_data(fd, &data);
+        auto ret = adf_get_overlay_engine_data(fd, &data);
         close(fd);
         if (ret < 0)
-            goto done;
+            return ret;
 
-        if (!filter(&data, cookie))
-            continue;
-
-        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
-        if (!new_ids) {
-            ret = -ENOMEM;
-            goto done;
-        }
-
-        ids_ret = new_ids;
-        ids_ret[n] = in[i];
-        n++;
+        if (filter(&data, cookie))
+            ids.push_back(in[i]);
     }
 
-    ret = n;
-
-done:
-    if (ret < 0)
-        free(ids_ret);
-    else
-        *out = ids_ret;
-    return ret;
+    return adf_id_vector_to_array(ids, out);
 }
 
 struct format_filter_cookie {
@@ -548,7 +490,7 @@
 static bool adf_overlay_engine_format_filter(
         struct adf_overlay_engine_data *data, void *cookie)
 {
-    struct format_filter_cookie *c = cookie;
+    auto c = static_cast<format_filter_cookie *>(cookie);
     size_t i;
     for (i = 0; i < data->n_supported_formats; i++) {
         size_t j;
@@ -592,35 +534,24 @@
     if (err < 0)
         return -errno;
 
-    if (data->n_supported_formats) {
-        data->supported_formats = malloc(sizeof(data->supported_formats[0]) *
-              data->n_supported_formats);
-        if (!data->supported_formats)
-            return -ENOMEM;
-    }
+    if (data->n_supported_formats)
+        data->supported_formats = new __u32[data->n_supported_formats];
 
-    if (data->custom_data_size) {
-      data->custom_data = malloc(data->custom_data_size);
-      if (!data->custom_data) {
-          ret = -ENOMEM;
-          goto done;
-      }
-    }
+    if (data->custom_data_size)
+      data->custom_data = new char[data->custom_data_size];
 
     err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data);
-    if (err < 0)
+    if (err < 0) {
         ret = -errno;
-
-done:
-    if (ret < 0)
         adf_free_overlay_engine_data(data);
+    }
     return ret;
 }
 
 void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data)
 {
-    free(data->supported_formats);
-    free(data->custom_data);
+    delete [] data->supported_formats;
+    delete [] static_cast<char *>(data->custom_data);
 }
 
 bool adf_overlay_engine_supports_format(int fd, __u32 format)
@@ -660,12 +591,12 @@
 int adf_read_event(int fd, struct adf_event **event)
 {
     struct adf_event header;
-    struct {
+    struct event_with_data {
         struct adf_event base;
         uint8_t data[0];
-    } *event_ret;
+    };
+    using unique_event = std::unique_ptr<event_with_data, decltype(&free)>;
     size_t data_size;
-    int ret = 0;
 
     int err = read(fd, &header, sizeof(header));
     if (err < 0)
@@ -675,28 +606,23 @@
     if (header.length < sizeof(header))
         return -EIO;
 
-    event_ret = malloc(header.length);
+    // Again, we can't use new[] since the existing API says the caller should
+    // free() the returned event
+    auto event_ptr = static_cast<event_with_data *>(malloc(header.length));
+    unique_event event_ret{event_ptr, free};
     if (!event_ret)
         return -ENOMEM;
     data_size = header.length - sizeof(header);
 
-    memcpy(event_ret, &header, sizeof(header));
+    memcpy(event_ret.get(), &header, sizeof(header));
     ssize_t read_size = read(fd, &event_ret->data, data_size);
-    if (read_size < 0) {
-        ret = -errno;
-        goto done;
-    }
-    if ((size_t)read_size < data_size) {
-        ret = -EIO;
-        goto done;
-    }
+    if (read_size < 0)
+        return -errno;
+    if ((size_t)read_size < data_size)
+        return -EIO;
 
-    *event = &event_ret->base;
-
-done:
-    if (ret < 0)
-        free(event_ret);
-    return ret;
+    *event = &event_ret.release()->base;
+    return 0;
 }
 
 void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE])
diff --git a/adf/libadf/include/adf/adf.h b/adf/libadf/include/adf/adf.h
index b6bda34..e4c7b28 100644
--- a/adf/libadf/include/adf/adf.h
+++ b/adf/libadf/include/adf/adf.h
@@ -75,6 +75,29 @@
         struct adf_buffer_config *bufs, size_t n_bufs,
         void *custom_data, size_t custom_data_size);
 /**
+ * Atomically posts a new display configuration to the specified interfaces.
+ *
+ * Compared to adf_device_post(), adf_device_post_v2():
+ *
+ *  (*) allows the client to choose the kind of sync fence returned
+ *      (through complete_fence_type)
+ *
+ *  (*) stores the returned sync fence fd in a provided buffer, so the client
+ *      can distinguish between a permission error (ret = -1) and a successful
+ *      call that returns no fence (*complete_fence = -1)
+ *
+ * On error, returns -errno.
+ *
+ * On devices without the corresponding kernel support, returns -ENOTTY.
+ */
+int adf_device_post_v2(struct adf_device *dev,
+        adf_id_t *interfaces, __u32 n_interfaces,
+        struct adf_buffer_config *bufs, __u32 n_bufs,
+        void *custom_data, __u64 custom_data_size,
+        enum adf_complete_fence_type complete_fence_type,
+        int *complete_fence);
+
+/**
  * Attaches the specified interface and overlay engine.
  */
 int adf_device_attach(struct adf_device *dev, adf_id_t overlay_engine,
@@ -163,6 +186,28 @@
 int adf_interface_simple_post(int fd, adf_id_t overlay_engine,
         __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
         __u32 pitch, int acquire_fence);
+/**
+ * Posts a single-plane RGB buffer to the display using the specified
+ * overlay engine.
+ *
+ * Compared to adf_interface_simple_post(), adf_interface_simple_post_v2():
+ *
+ *  (*) allows the client to choose the kind of sync fence returned
+ *      (through complete_fence_type)
+ *
+ *  (*) stores the returned sync fence fd in a provided buffer, so the client
+ *      can distinguish between a permission error (ret = -1) and a successful
+ *      call that returns no fence (*complete_fence = -1)
+ *
+ * On error, returns -errno.
+ *
+ * On devices without the corresponding kernel support, returns -ENOTTY.
+ */
+int adf_interface_simple_post_v2(int fd, adf_id_t overlay_engine,
+        __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
+        __u32 pitch, int acquire_fence,
+        enum adf_complete_fence_type complete_fence_type,
+        int *complete_fence);
 
 /**
  * Enumerates all overlay engines belonging to an ADF device.
diff --git a/adf/libadf/include/video/adf.h b/adf/libadf/include/video/adf.h
new file mode 100644
index 0000000..692a425
--- /dev/null
+++ b/adf/libadf/include/video/adf.h
@@ -0,0 +1,209 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_VIDEO_ADF_H_
+#define _UAPI_VIDEO_ADF_H_
+#include <linux/ioctl.h>
+#include <linux/types.h>
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#include <drm/drm_fourcc.h>
+#include <drm/drm_mode.h>
+#define ADF_NAME_LEN 32
+#define ADF_MAX_CUSTOM_DATA_SIZE 4096
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+enum adf_interface_type {
+  ADF_INTF_DSI = 0,
+  ADF_INTF_eDP = 1,
+  ADF_INTF_DPI = 2,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  ADF_INTF_VGA = 3,
+  ADF_INTF_DVI = 4,
+  ADF_INTF_HDMI = 5,
+  ADF_INTF_MEMORY = 6,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  ADF_INTF_TYPE_DEVICE_CUSTOM = 128,
+  ADF_INTF_TYPE_MAX = (~(__u32) 0),
+};
+#define ADF_INTF_FLAG_PRIMARY (1 << 0)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_INTF_FLAG_EXTERNAL (1 << 1)
+enum adf_event_type {
+  ADF_EVENT_VSYNC = 0,
+  ADF_EVENT_HOTPLUG = 1,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  ADF_EVENT_DEVICE_CUSTOM = 128,
+  ADF_EVENT_TYPE_MAX = 255,
+};
+enum adf_complete_fence_type {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  ADF_COMPLETE_FENCE_NONE = 0,
+  ADF_COMPLETE_FENCE_PRESENT = 1,
+  ADF_COMPLETE_FENCE_RELEASE = 2,
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_set_event {
+  __u8 type;
+  __u8 enabled;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_event {
+  __u8 type;
+  __u32 length;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_vsync_event {
+  struct adf_event base;
+  __aligned_u64 timestamp;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_hotplug_event {
+  struct adf_event base;
+  __u8 connected;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_MAX_PLANES 4
+struct adf_buffer_config {
+  __u32 overlay_engine;
+  __u32 w;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u32 h;
+  __u32 format;
+  __s32 fd[ADF_MAX_PLANES];
+  __u32 offset[ADF_MAX_PLANES];
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u32 pitch[ADF_MAX_PLANES];
+  __u8 n_planes;
+  __s32 acquire_fence;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_MAX_BUFFERS (4096 / sizeof(struct adf_buffer_config))
+struct adf_post_config {
+  size_t n_interfaces;
+  __u32 __user * interfaces;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  size_t n_bufs;
+  struct adf_buffer_config __user * bufs;
+  size_t custom_data_size;
+  void __user * custom_data;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __s32 complete_fence;
+};
+struct adf_post_config_v2 {
+  __u32 n_interfaces;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u64 interfaces;
+  __u32 n_bufs;
+  __u64 bufs;
+  __u64 custom_data_size;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u64 custom_data;
+  __s32 complete_fence;
+  __u8 complete_fence_type;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_MAX_INTERFACES (4096 / sizeof(__u32))
+struct adf_simple_buffer_alloc {
+  __u16 w;
+  __u16 h;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u32 format;
+  __s32 fd;
+  __u32 offset;
+  __u32 pitch;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct adf_simple_post_config {
+  struct adf_buffer_config buf;
+  __s32 complete_fence;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct adf_simple_post_config_v2 {
+  struct adf_buffer_config buf;
+  __s32 complete_fence;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u8 complete_fence_type;
+};
+struct adf_attachment_config {
+  __u32 overlay_engine;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u32 interface;
+};
+struct adf_device_data {
+  char name[ADF_NAME_LEN];
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  size_t n_attachments;
+  struct adf_attachment_config __user * attachments;
+  size_t n_allowed_attachments;
+  struct adf_attachment_config __user * allowed_attachments;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  size_t custom_data_size;
+  void __user * custom_data;
+};
+#define ADF_MAX_ATTACHMENTS (4096 / sizeof(struct adf_attachment_config))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_interface_data {
+  char name[ADF_NAME_LEN];
+  __u32 type;
+  __u32 id;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u32 flags;
+  __u8 dpms_state;
+  __u8 hotplug_detect;
+  __u16 width_mm;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u16 height_mm;
+  struct drm_mode_modeinfo current_mode;
+  size_t n_available_modes;
+  struct drm_mode_modeinfo __user * available_modes;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  size_t custom_data_size;
+  void __user * custom_data;
+};
+#define ADF_MAX_MODES (4096 / sizeof(struct drm_mode_modeinfo))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_overlay_engine_data {
+  char name[ADF_NAME_LEN];
+  size_t n_supported_formats;
+  __u32 __user * supported_formats;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  size_t custom_data_size;
+  void __user * custom_data;
+};
+#define ADF_MAX_SUPPORTED_FORMATS (4096 / sizeof(__u32))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_IOCTL_TYPE 'D'
+#define ADF_IOCTL_NR_CUSTOM 128
+#define ADF_SET_EVENT _IOW(ADF_IOCTL_TYPE, 0, struct adf_set_event)
+#define ADF_BLANK _IOW(ADF_IOCTL_TYPE, 1, __u8)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 2, struct adf_post_config)
+#define ADF_SET_MODE _IOW(ADF_IOCTL_TYPE, 3, struct drm_mode_modeinfo)
+#define ADF_GET_DEVICE_DATA _IOR(ADF_IOCTL_TYPE, 4, struct adf_device_data)
+#define ADF_GET_INTERFACE_DATA _IOR(ADF_IOCTL_TYPE, 5, struct adf_interface_data)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_GET_OVERLAY_ENGINE_DATA _IOR(ADF_IOCTL_TYPE, 6, struct adf_overlay_engine_data)
+#define ADF_SIMPLE_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 7, struct adf_simple_post_config)
+#define ADF_SIMPLE_BUFFER_ALLOC _IOW(ADF_IOCTL_TYPE, 8, struct adf_simple_buffer_alloc)
+#define ADF_ATTACH _IOW(ADF_IOCTL_TYPE, 9, struct adf_attachment_config)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_DETACH _IOW(ADF_IOCTL_TYPE, 10, struct adf_attachment_config)
+#define ADF_POST_CONFIG_V2 _IOW(ADF_IOCTL_TYPE, 11, struct adf_post_config_v2)
+#define ADF_SIMPLE_POST_CONFIG_V2 _IOW(ADF_IOCTL_TYPE, 12, struct adf_simple_post_config_v2)
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+
diff --git a/adf/libadf/original-kernel-headers/video/adf.h b/adf/libadf/original-kernel-headers/video/adf.h
new file mode 100644
index 0000000..8293c1d
--- /dev/null
+++ b/adf/libadf/original-kernel-headers/video/adf.h
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_VIDEO_ADF_H_
+#define _UAPI_VIDEO_ADF_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_mode.h>
+
+#define ADF_NAME_LEN 32
+#define ADF_MAX_CUSTOM_DATA_SIZE 4096
+
+enum adf_interface_type {
+	ADF_INTF_DSI = 0,
+	ADF_INTF_eDP = 1,
+	ADF_INTF_DPI = 2,
+	ADF_INTF_VGA = 3,
+	ADF_INTF_DVI = 4,
+	ADF_INTF_HDMI = 5,
+	ADF_INTF_MEMORY = 6,
+	ADF_INTF_TYPE_DEVICE_CUSTOM = 128,
+	ADF_INTF_TYPE_MAX = (~(__u32)0),
+};
+
+#define ADF_INTF_FLAG_PRIMARY (1 << 0)
+#define ADF_INTF_FLAG_EXTERNAL (1 << 1)
+
+enum adf_event_type {
+	ADF_EVENT_VSYNC = 0,
+	ADF_EVENT_HOTPLUG = 1,
+	ADF_EVENT_DEVICE_CUSTOM = 128,
+	ADF_EVENT_TYPE_MAX = 255,
+};
+
+enum adf_complete_fence_type {
+	/* no fence */
+	ADF_COMPLETE_FENCE_NONE = 0,
+	/* fence fires when the configuration appears on the screen */
+	ADF_COMPLETE_FENCE_PRESENT = 1,
+	/* fence fires when the configuration leaves the screen */
+	ADF_COMPLETE_FENCE_RELEASE = 2,
+};
+
+/**
+ * struct adf_set_event - start or stop subscribing to ADF events
+ *
+ * @type: the type of event to (un)subscribe
+ * @enabled: subscribe or unsubscribe
+ *
+ * After subscribing to an event, userspace may poll() the ADF object's fd
+ * to wait for events or read() to consume the event's data.
+ *
+ * ADF reserves event types 0 to %ADF_EVENT_DEVICE_CUSTOM-1 for its own events.
+ * Devices may use event types %ADF_EVENT_DEVICE_CUSTOM to %ADF_EVENT_TYPE_MAX-1
+ * for driver-private events.
+ */
+struct adf_set_event {
+	__u8 type;
+	__u8 enabled;
+};
+
+/**
+ * struct adf_event - common header for ADF event data
+ *
+ * @type: event type
+ * @length: total size of event data, header inclusive
+ */
+struct adf_event {
+	__u8 type;
+	__u32 length;
+};
+
+/**
+ * struct adf_vsync_event - ADF vsync event
+ *
+ * @base: event header (see &struct adf_event)
+ * @timestamp: time of vsync event, in nanoseconds
+ */
+struct adf_vsync_event {
+	struct adf_event base;
+	__aligned_u64 timestamp;
+};
+
+/**
+ * struct adf_vsync_event - ADF display hotplug event
+ *
+ * @base: event header (see &struct adf_event)
+ * @connected: whether a display is now connected to the interface
+ */
+struct adf_hotplug_event {
+	struct adf_event base;
+	__u8 connected;
+};
+
+#define ADF_MAX_PLANES 4
+/**
+ * struct adf_buffer_config - description of buffer displayed by adf_post_config
+ *
+ * @overlay_engine: id of the target overlay engine
+ * @w: width of display region in pixels
+ * @h: height of display region in pixels
+ * @format: DRM-style fourcc, see drm_fourcc.h for standard formats
+ * @fd: dma_buf fd for each plane
+ * @offset: location of first pixel to scan out, in bytes
+ * @pitch: stride (i.e. length of a scanline including padding) in bytes
+ * @n_planes: number of planes in buffer
+ * @acquire_fence: sync_fence fd which will clear when the buffer is
+ *	ready for display, or <0 if the buffer is already ready
+ */
+struct adf_buffer_config {
+	__u32 overlay_engine;
+
+	__u32 w;
+	__u32 h;
+	__u32 format;
+
+	__s32 fd[ADF_MAX_PLANES];
+	__u32 offset[ADF_MAX_PLANES];
+	__u32 pitch[ADF_MAX_PLANES];
+	__u8 n_planes;
+
+	__s32 acquire_fence;
+};
+#define ADF_MAX_BUFFERS (4096 / sizeof(struct adf_buffer_config))
+
+/**
+ * struct adf_post_config - request to flip to a new set of buffers
+ *
+ * This request is equivalent to &struct adf_post_config_v2 with
+ * @complete_fence_type = %ADF_COMPLETE_FENCE_RELEASE.
+ *
+ * @n_interfaces: number of interfaces targeted by the flip (input)
+ * @interfaces: ids of interfaces targeted by the flip (input)
+ * @n_bufs: number of buffers displayed (input)
+ * @bufs: description of buffers displayed (input)
+ * @custom_data_size: size of driver-private data (input)
+ * @custom_data: driver-private data (input)
+ * @complete_fence: sync_fence fd which will clear when this
+ *	configuration has left the screen (output)
+ */
+struct adf_post_config {
+	size_t n_interfaces;
+	__u32 __user *interfaces;
+
+	size_t n_bufs;
+	struct adf_buffer_config __user *bufs;
+
+	size_t custom_data_size;
+	void __user *custom_data;
+
+	__s32 complete_fence;
+};
+
+/**
+ * struct adf_post_config_v2 - request to flip to a new set of buffers
+ *
+ * @n_interfaces: number of interfaces targeted by the flip (input)
+ * @interfaces: ids of interfaces targeted by the flip (input)
+ * @n_bufs: number of buffers displayed (input)
+ * @bufs: description of buffers displayed (input)
+ * @custom_data_size: size of driver-private data (input)
+ * @custom_data: driver-private data (input)
+ * @complete_fence_type: one of &enum adf_complete_fence_type describing what
+ * 	fence to return (input)
+ * @complete_fence: sync_fence fd which will fire at the time
+ * 	requested by @complete_fence_type (output)
+ */
+struct adf_post_config_v2 {
+	__u32 n_interfaces;
+	__u64 interfaces; /* __u32 * packed into __u64 */
+
+	__u32 n_bufs;
+	__u64 bufs; /* struct adf_buffer_config * packed into __u64 */
+
+	__u64 custom_data_size;
+	__u64 custom_data; /* void * packed into __u64 */
+
+	__s32 complete_fence;
+	__u8 complete_fence_type;
+};
+#define ADF_MAX_INTERFACES (4096 / sizeof(__u32))
+
+/**
+ * struct adf_simple_buffer_allocate - request to allocate a "simple" buffer
+ *
+ * @w: width of buffer in pixels (input)
+ * @h: height of buffer in pixels (input)
+ * @format: DRM-style fourcc (input)
+ *
+ * @fd: dma_buf fd (output)
+ * @offset: location of first pixel, in bytes (output)
+ * @pitch: length of a scanline including padding, in bytes (output)
+ *
+ * Simple buffers are analogous to DRM's "dumb" buffers.  They have a single
+ * plane of linear RGB data which can be allocated and scanned out without
+ * any driver-private ioctls or data.
+ *
+ * @format must be a standard RGB format defined in drm_fourcc.h.
+ *
+ * ADF clients must NOT assume that an interface can scan out a simple buffer
+ * allocated by a different ADF interface, even if the two interfaces belong to
+ * the same ADF device.
+ */
+struct adf_simple_buffer_alloc {
+	__u16 w;
+	__u16 h;
+	__u32 format;
+
+	__s32 fd;
+	__u32 offset;
+	__u32 pitch;
+};
+
+/**
+ * struct adf_simple_post_config - request to flip to a single buffer without
+ * driver-private data
+ *
+ * This request is equivalent to &struct adf_simple_post_config_v2 with
+ * @complete_fence_type = %ADF_COMPLETE_FENCE_RELEASE.
+ *
+ * @buf: description of buffer displayed (input)
+ * @complete_fence: sync_fence fd which will clear when this buffer has left the
+ * screen (output)
+ */
+struct adf_simple_post_config {
+	struct adf_buffer_config buf;
+	__s32 complete_fence;
+};
+
+/**
+ * struct adf_simple_post_config_v2 - request to flip to a single buffer without
+ * driver-private data
+ *
+ * @buf: description of buffer displayed (input)
+ * @complete_fence_type: one of &enum adf_complete_fence_type describing what
+ * 	fence to return (input)
+ * @complete_fence: sync_fence fd which will fire at the time
+ * 	requested by @complete_fence_type (output)
+ */
+struct adf_simple_post_config_v2 {
+	struct adf_buffer_config buf;
+	__s32 complete_fence;
+	__u8 complete_fence_type;
+};
+
+/**
+ * struct adf_attachment_config - description of attachment between an overlay
+ * engine and an interface
+ *
+ * @overlay_engine: id of the overlay engine
+ * @interface: id of the interface
+ */
+struct adf_attachment_config {
+	__u32 overlay_engine;
+	__u32 interface;
+};
+
+/**
+ * struct adf_device_data - describes a display device
+ *
+ * @name: display device's name
+ * @n_attachments: the number of current attachments
+ * @attachments: list of current attachments
+ * @n_allowed_attachments: the number of allowed attachments
+ * @allowed_attachments: list of allowed attachments
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ */
+struct adf_device_data {
+	char name[ADF_NAME_LEN];
+
+	size_t n_attachments;
+	struct adf_attachment_config __user *attachments;
+
+	size_t n_allowed_attachments;
+	struct adf_attachment_config __user *allowed_attachments;
+
+	size_t custom_data_size;
+	void __user *custom_data;
+};
+#define ADF_MAX_ATTACHMENTS (4096 / sizeof(struct adf_attachment_config))
+
+/**
+ * struct adf_device_data - describes a display interface
+ *
+ * @name: display interface's name
+ * @type: interface type (see enum @adf_interface_type)
+ * @id: which interface of type @type;
+ *	e.g. interface DSI.1 -> @type=@ADF_INTF_TYPE_DSI, @id=1
+ * @flags: informational flags (bitmask of %ADF_INTF_FLAG_* values)
+ * @dpms_state: DPMS state (one of @DRM_MODE_DPMS_* defined in drm_mode.h)
+ * @hotplug_detect: whether a display is plugged in
+ * @width_mm: screen width in millimeters, or 0 if unknown
+ * @height_mm: screen height in millimeters, or 0 if unknown
+ * @current_mode: current display mode
+ * @n_available_modes: the number of hardware display modes
+ * @available_modes: list of hardware display modes
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ */
+struct adf_interface_data {
+	char name[ADF_NAME_LEN];
+
+	__u32 type;
+	__u32 id;
+	/* e.g. type=ADF_INTF_TYPE_DSI, id=1 => DSI.1 */
+	__u32 flags;
+
+	__u8 dpms_state;
+	__u8 hotplug_detect;
+	__u16 width_mm;
+	__u16 height_mm;
+
+	struct drm_mode_modeinfo current_mode;
+	size_t n_available_modes;
+	struct drm_mode_modeinfo __user *available_modes;
+
+	size_t custom_data_size;
+	void __user *custom_data;
+};
+#define ADF_MAX_MODES (4096 / sizeof(struct drm_mode_modeinfo))
+
+/**
+ * struct adf_overlay_engine_data - describes an overlay engine
+ *
+ * @name: overlay engine's name
+ * @n_supported_formats: number of supported formats
+ * @supported_formats: list of supported formats
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ */
+struct adf_overlay_engine_data {
+	char name[ADF_NAME_LEN];
+
+	size_t n_supported_formats;
+	__u32 __user *supported_formats;
+
+	size_t custom_data_size;
+	void __user *custom_data;
+};
+#define ADF_MAX_SUPPORTED_FORMATS (4096 / sizeof(__u32))
+
+#define ADF_IOCTL_TYPE		'D'
+#define ADF_IOCTL_NR_CUSTOM	128
+
+#define ADF_SET_EVENT		_IOW(ADF_IOCTL_TYPE, 0, struct adf_set_event)
+#define ADF_BLANK		_IOW(ADF_IOCTL_TYPE, 1, __u8)
+#define ADF_POST_CONFIG		_IOW(ADF_IOCTL_TYPE, 2, struct adf_post_config)
+#define ADF_SET_MODE		_IOW(ADF_IOCTL_TYPE, 3, \
+					struct drm_mode_modeinfo)
+#define ADF_GET_DEVICE_DATA	_IOR(ADF_IOCTL_TYPE, 4, struct adf_device_data)
+#define ADF_GET_INTERFACE_DATA	_IOR(ADF_IOCTL_TYPE, 5, \
+					struct adf_interface_data)
+#define ADF_GET_OVERLAY_ENGINE_DATA \
+				_IOR(ADF_IOCTL_TYPE, 6, \
+					struct adf_overlay_engine_data)
+#define ADF_SIMPLE_POST_CONFIG	_IOW(ADF_IOCTL_TYPE, 7, \
+					struct adf_simple_post_config)
+#define ADF_SIMPLE_BUFFER_ALLOC	_IOW(ADF_IOCTL_TYPE, 8, \
+					struct adf_simple_buffer_alloc)
+#define ADF_ATTACH		_IOW(ADF_IOCTL_TYPE, 9, \
+					struct adf_attachment_config)
+#define ADF_DETACH		_IOW(ADF_IOCTL_TYPE, 10, \
+					struct adf_attachment_config)
+
+#define ADF_POST_CONFIG_V2	_IOW(ADF_IOCTL_TYPE, 11, \
+					struct adf_post_config_v2)
+#define ADF_SIMPLE_POST_CONFIG_V2 \
+				_IOW(ADF_IOCTL_TYPE, 12, \
+					struct adf_simple_post_config_v2)
+
+#endif /* _UAPI_VIDEO_ADF_H_ */
diff --git a/adf/libadf/tests/Android.bp b/adf/libadf/tests/Android.bp
index 7b33300..9b3430e 100644
--- a/adf/libadf/tests/Android.bp
+++ b/adf/libadf/tests/Android.bp
@@ -17,6 +17,7 @@
 cc_test {
     name: "adf-unit-tests",
     srcs: ["adf_test.cpp"],
+    shared_libs: ["libsync"],
     static_libs: ["libadf"],
     cflags: ["-Werror"],
 }
diff --git a/adf/libadf/tests/adf_test.cpp b/adf/libadf/tests/adf_test.cpp
index 01b2785..82a91f4 100644
--- a/adf/libadf/tests/adf_test.cpp
+++ b/adf/libadf/tests/adf_test.cpp
@@ -20,6 +20,7 @@
 #include <adf/adf.h>
 #include <gtest/gtest.h>
 #include <sys/mman.h>
+#include <sync/sync.h>
 
 class AdfTest : public testing::Test {
 public:
@@ -73,24 +74,6 @@
         FAIL(); /* this should never happen */
     }
 
-    void drawCheckerboard(void *buf, uint32_t w, uint32_t h, uint32_t pitch) {
-        uint8_t *buf8 = reinterpret_cast<uint8_t *>(buf);
-        for (uint32_t y = 0; y < h / 2; y++) {
-            uint32_t *scanline = reinterpret_cast<uint32_t *>(buf8 + y * pitch);
-            for (uint32_t x = 0; x < w / 2; x++)
-                scanline[x] = 0xFF0000FF;
-            for (uint32_t x = w / 2; x < w; x++)
-                scanline[x] = 0xFF00FFFF;
-        }
-        for (uint32_t y = h / 2; y < h; y++) {
-            uint32_t *scanline = reinterpret_cast<uint32_t *>(buf8 + y * pitch);
-            for (uint32_t x = 0; x < w / 2; x++)
-                scanline[x] = 0xFFFF00FF;
-            for (uint32_t x = w / 2; x < w; x++)
-                scanline[x] = 0xFFFFFFFF;
-        }
-    }
-
     /* various helpers to call ADF and die on failure */
 
     void getInterfaceData(adf_interface_data &data) {
@@ -141,6 +124,42 @@
         free(event);
     }
 
+    void drawCheckerboard(uint32_t &w, uint32_t &h, uint32_t &format,
+            char format_str[ADF_FORMAT_STR_SIZE], int &buf_fd, uint32_t &offset,
+            uint32_t &pitch) {
+        ASSERT_NO_FATAL_FAILURE(getCurrentMode(w, h));
+        ASSERT_NO_FATAL_FAILURE(get8888Format(format, format_str));
+
+        buf_fd = adf_interface_simple_buffer_alloc(intf, w, h, format, &offset,
+                &pitch);
+        ASSERT_GE(buf_fd, 0) << "allocating " << w << "x" << h << " " <<
+                format_str << " buffer failed: " << strerror(-buf_fd);
+        EXPECT_GE(pitch, w * 4);
+
+        void *mapped = mmap(NULL, pitch * h, PROT_WRITE, MAP_SHARED, buf_fd,
+                offset);
+        ASSERT_NE(mapped, MAP_FAILED) << "mapping " << w << "x" << h << " " <<
+                format_str << " buffer failed: " << strerror(-errno);
+
+        uint8_t *buf8 = static_cast<uint8_t *>(mapped);
+        for (uint32_t y = 0; y < h / 2; y++) {
+            uint32_t *scanline = reinterpret_cast<uint32_t *>(buf8 + y * pitch);
+            for (uint32_t x = 0; x < w / 2; x++)
+                scanline[x] = 0xFF0000FF;
+            for (uint32_t x = w / 2; x < w; x++)
+                scanline[x] = 0xFF00FFFF;
+        }
+        for (uint32_t y = h / 2; y < h; y++) {
+            uint32_t *scanline = reinterpret_cast<uint32_t *>(buf8 + y * pitch);
+            for (uint32_t x = 0; x < w / 2; x++)
+                scanline[x] = 0xFFFF00FF;
+            for (uint32_t x = w / 2; x < w; x++)
+                scanline[x] = 0xFFFFFFFF;
+        }
+
+        munmap(mapped, pitch * h);
+    }
+
 protected:
     adf_device dev;
     adf_id_t intf_id;
@@ -149,11 +168,13 @@
     int eng;
 
 private:
-    const static adf_id_t dev_id = 0;
+    const static adf_id_t dev_id;
     const static __u32 fmt8888[];
     const static size_t n_fmt8888;
 };
 
+const adf_id_t AdfTest::dev_id = 0;
+
 const __u32 AdfTest::fmt8888[] = {
    DRM_FORMAT_XRGB8888,
    DRM_FORMAT_XBGR8888,
@@ -167,7 +188,7 @@
 const size_t AdfTest::n_fmt8888 = sizeof(fmt8888) / sizeof(fmt8888[0]);
 
 TEST(adf, devices) {
-    adf_id_t *devs;
+    adf_id_t *devs = nullptr;
     ssize_t n_devs = adf_devices(&devs);
     free(devs);
 
@@ -308,27 +329,11 @@
 }
 
 TEST_F(AdfTest, simple_buffer) {
-    uint32_t w = 0, h = 0;
-    ASSERT_NO_FATAL_FAILURE(getCurrentMode(w, h));
-
-    uint32_t format = 0;
+    int buf_fd;
+    uint32_t w, h, format, offset, pitch;
     char format_str[ADF_FORMAT_STR_SIZE];
-    ASSERT_NO_FATAL_FAILURE(get8888Format(format, format_str));
-
-    uint32_t offset;
-    uint32_t pitch;
-    int buf_fd = adf_interface_simple_buffer_alloc(intf, w, h, format, &offset,
-            &pitch);
-    ASSERT_GE(buf_fd, 0) << "allocating " << w << "x" << h << " " <<
-            format_str << " buffer failed: " << strerror(-buf_fd);
-    EXPECT_GE(pitch, w * 4);
-
-    void *mapped = mmap(NULL, pitch * h, PROT_WRITE, MAP_SHARED, buf_fd,
-            offset);
-    ASSERT_NE(mapped, MAP_FAILED) << "mapping " << w << "x" << h << " " <<
-            format_str << " buffer failed: " << strerror(-errno);
-    drawCheckerboard(mapped, w, h, pitch);
-    munmap(mapped, pitch * h);
+    ASSERT_NO_FATAL_FAILURE(drawCheckerboard(w, h, format, format_str,
+            buf_fd, offset, pitch));
 
     ASSERT_NO_FATAL_FAILURE(attach());
     ASSERT_NO_FATAL_FAILURE(blank(DRM_MODE_DPMS_ON));
@@ -340,3 +345,59 @@
             format_str << " buffer failed: " << strerror(-release_fence);
     close(release_fence);
 }
+
+TEST_F(AdfTest, simple_buffer_v2) {
+    int buf_fd;
+    uint32_t w, h, format, offset, pitch;
+    char format_str[ADF_FORMAT_STR_SIZE];
+    ASSERT_NO_FATAL_FAILURE(drawCheckerboard(w, h, format, format_str,
+            buf_fd, offset, pitch));
+
+    ASSERT_NO_FATAL_FAILURE(attach());
+    ASSERT_NO_FATAL_FAILURE(blank(DRM_MODE_DPMS_ON));
+
+    int config_1_release;
+    int err = adf_interface_simple_post_v2(intf, eng_id, w, h,
+            format, buf_fd, offset, pitch, -1, ADF_COMPLETE_FENCE_RELEASE,
+            &config_1_release);
+    if (err == -ENOTTY) {
+        GTEST_LOG_(INFO) << "ADF_SIMPLE_POST_CONFIG_V2 not supported on this kernel";
+        return;
+    }
+    ASSERT_GE(err, 0) << "posting " << w << "x" << h << " " <<
+            format_str << " buffer failed: " << strerror(-err);
+
+    err = sync_wait(config_1_release, 1000);
+    ASSERT_EQ(-1, err) <<
+            "waiting for config 1's release fence should not have suceeded";
+    ASSERT_EQ(ETIME, errno) <<
+            "config 1's release fence should have timed out, but failed instead: " <<
+            strerror(errno);
+
+    int config_2_present;
+    err = adf_interface_simple_post_v2(intf, eng_id, w, h,
+            format, buf_fd, offset, pitch, -1, ADF_COMPLETE_FENCE_PRESENT,
+            &config_2_present);
+    ASSERT_GE(err, 0) << "posting " << w << "x" << h << " " <<
+            format_str << " buffer failed: " << strerror(-err);
+
+    err = sync_wait(config_2_present, 1000);
+    ASSERT_EQ(0, err) <<
+            "waiting for config 2's present fence failed: " << strerror(errno);
+    err = sync_wait(config_1_release, 0);
+    ASSERT_EQ(0, err) <<
+            "waiting for config 1's release fence failed: " << strerror(errno);
+    close(config_1_release);
+    close(config_2_present);
+
+    int config_3_no_fence;
+    err = adf_interface_simple_post_v2(intf, eng_id, w, h,
+            format, buf_fd, offset, pitch, -1, ADF_COMPLETE_FENCE_NONE,
+            &config_3_no_fence);
+    ASSERT_GE(err, 0) << "posting " << w << "x" << h << " " <<
+            format_str << " buffer failed: " << strerror(-err);
+    ASSERT_EQ(-1, config_3_no_fence) <<
+            "fence returned even though the fence type was ADF_COMPLETE_FENCE_NONE";
+
+    close(buf_fd);
+}
diff --git a/adf/libadfhwc/adfhwc.cpp b/adf/libadfhwc/adfhwc.cpp
index 7d5b555..63c0f75 100644
--- a/adf/libadfhwc/adfhwc.cpp
+++ b/adf/libadfhwc/adfhwc.cpp
@@ -20,7 +20,7 @@
 #include <pthread.h>
 #include <sys/resource.h>
 
-#include <android/log.h>
+#include <log/log.h>
 #include <utils/Vector.h>
 
 #include <adf/adf.h>
@@ -166,6 +166,71 @@
     return 0;
 }
 
+static int32_t adf_display_attribute_hwc2(const adf_interface_data &data,
+        const drm_mode_modeinfo &mode, const uint32_t attribute)
+{
+    switch (attribute) {
+    case HWC2_ATTRIBUTE_VSYNC_PERIOD:
+        if (mode.vrefresh)
+            return 1000000000 / mode.vrefresh;
+        return 0;
+
+    case HWC2_ATTRIBUTE_WIDTH:
+        return mode.hdisplay;
+
+    case HWC2_ATTRIBUTE_HEIGHT:
+        return mode.vdisplay;
+
+    case HWC2_ATTRIBUTE_DPI_X:
+        return dpi(mode.hdisplay, data.width_mm);
+
+    case HWC2_ATTRIBUTE_DPI_Y:
+        return dpi(mode.vdisplay, data.height_mm);
+
+    default:
+        ALOGE("unknown display attribute %u", attribute);
+        return -EINVAL;
+    }
+}
+
+int adf_getDisplayAttributes_hwc2(struct adf_hwc_helper *dev, int disp,
+        uint32_t config, const uint32_t *attributes, int32_t *values)
+{
+    if ((size_t)disp >= dev->intf_fds.size())
+        return -EINVAL;
+
+    if (config >= dev->display_configs.size())
+        return -EINVAL;
+
+    adf_interface_data data;
+    int err = adf_get_interface_data(dev->intf_fds[disp], &data);
+    if (err < 0) {
+        ALOGE("failed to get ADF interface data: %s", strerror(err));
+        return err;
+    }
+
+    for (int i = 0; attributes[i] != HWC2_ATTRIBUTE_INVALID; i++)
+        values[i] = adf_display_attribute_hwc2(data,
+                dev->display_configs[config], attributes[i]);
+
+    adf_free_interface_data(&data);
+    return 0;
+}
+
+int adf_set_active_config_hwc2(struct adf_hwc_helper *dev, int disp,
+        uint32_t config)
+{
+    if ((size_t)disp >= dev->intf_fds.size())
+        return -EINVAL;
+
+    if (config >= dev->display_configs.size())
+        return -EINVAL;
+
+    struct drm_mode_modeinfo mode = dev->display_configs[config];
+
+    return adf_interface_set_mode(dev->intf_fds[disp], &mode);
+}
+
 static void handle_adf_event(struct adf_hwc_helper *dev, int disp)
 {
     adf_event *event;
@@ -208,28 +273,39 @@
 
     setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
 
-    pollfd *fds = new pollfd[dev->intf_fds.size()];
+    struct sigaction action = { };
+    sigemptyset(&action.sa_mask);
+    action.sa_flags = 0;
+    action.sa_handler = [](int) { pthread_exit(0); };
+
+    if (sigaction(SIGUSR2, &action, NULL) < 0) {
+        ALOGE("failed to set thread exit action %s", strerror(errno));
+        return NULL;
+    }
+
+    sigset_t signal_set;
+    sigemptyset(&signal_set);
+    sigaddset(&signal_set, SIGUSR2);
+
+    pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
+
+    pollfd fds[dev->intf_fds.size()];
     for (size_t i = 0; i < dev->intf_fds.size(); i++) {
         fds[i].fd = dev->intf_fds[i];
         fds[i].events = POLLIN | POLLPRI;
     }
 
     while (true) {
-        int err = poll(fds, dev->intf_fds.size(), -1);
-
-        if (err > 0) {
-            for (size_t i = 0; i < dev->intf_fds.size(); i++)
-                if (fds[i].revents & (POLLIN | POLLPRI))
-                    handle_adf_event(dev, i);
-        }
-        else if (err == -1) {
-            if (errno == EINTR)
-                break;
+        if (TEMP_FAILURE_RETRY(poll(fds, dev->intf_fds.size(), -1)) < 0) {
             ALOGE("error in event thread: %s", strerror(errno));
+            break;
         }
+
+        for (size_t i = 0; i < dev->intf_fds.size(); i++)
+            if (fds[i].revents & (POLLIN | POLLPRI))
+                handle_adf_event(dev, i);
     }
 
-    delete [] fds;
     return NULL;
 }
 
@@ -264,6 +340,12 @@
         }
     }
 
+    sigset_t signal_set;
+    sigemptyset(&signal_set);
+    sigaddset(&signal_set, SIGUSR2);
+
+    pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
+
     ret = pthread_create(&dev_ret->event_thread, NULL, adf_event_thread,
             dev_ret);
     if (ret) {
@@ -284,7 +366,7 @@
 
 void adf_hwc_close(struct adf_hwc_helper *dev)
 {
-    pthread_kill(dev->event_thread, SIGTERM);
+    pthread_kill(dev->event_thread, SIGUSR2);
     pthread_join(dev->event_thread, NULL);
 
     for (size_t i = 0; i < dev->intf_fds.size(); i++)
diff --git a/adf/libadfhwc/include/adfhwc/adfhwc.h b/adf/libadfhwc/include/adfhwc/adfhwc.h
index 71e7624..4f70925 100644
--- a/adf/libadfhwc/include/adfhwc/adfhwc.h
+++ b/adf/libadfhwc/include/adfhwc/adfhwc.h
@@ -23,6 +23,7 @@
 #include <video/adf.h>
 
 #include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer2.h>
 
 struct adf_hwc_helper;
 
@@ -123,6 +124,16 @@
         uint32_t *configs, size_t *numConfigs);
 int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp,
         uint32_t config, const uint32_t *attributes, int32_t *values);
+/**
+ * Generic implementation of common HWC2 functions.
+ *
+ * The HWC2 should not return these functions directly through getFunction.
+ * Instead, the HWC2 should return stub functions which call these helpers.
+ */
+int adf_getDisplayAttributes_hwc2(struct adf_hwc_helper *dev, int disp,
+        uint32_t config, const uint32_t *attributes, int32_t *values);
+int adf_set_active_config_hwc2(struct adf_hwc_helper *dev, int disp,
+        uint32_t config);
 
 __END_DECLS
 
diff --git a/base/Android.bp b/base/Android.bp
index b9a6e0b..7b1dc8e 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -44,6 +44,9 @@
                 "properties.cpp",
             ],
             cppflags: ["-Wexit-time-destructors"],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
         },
         darwin: {
             srcs: ["errors_unix.cpp"],
@@ -75,6 +78,7 @@
     host_supported: true,
     clang: true,
     srcs: [
+        "endian_test.cpp",
         "errors_test.cpp",
         "file_test.cpp",
         "logging_test.cpp",
@@ -89,6 +93,9 @@
     target: {
         android: {
             srcs: ["properties_test.cpp"],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
         },
         windows: {
             srcs: ["utf8_test.cpp"],
diff --git a/base/endian_test.cpp b/base/endian_test.cpp
new file mode 100644
index 0000000..963ab13
--- /dev/null
+++ b/base/endian_test.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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/endian.h"
+
+#include <gtest/gtest.h>
+
+TEST(endian, constants) {
+  ASSERT_TRUE(__LITTLE_ENDIAN == LITTLE_ENDIAN);
+  ASSERT_TRUE(__BIG_ENDIAN == BIG_ENDIAN);
+  ASSERT_TRUE(__BYTE_ORDER == BYTE_ORDER);
+
+  ASSERT_EQ(__LITTLE_ENDIAN, __BYTE_ORDER);
+}
+
+TEST(endian, smoke) {
+  static constexpr uint16_t le16 = 0x1234;
+  static constexpr uint32_t le32 = 0x12345678;
+  static constexpr uint64_t le64 = 0x123456789abcdef0;
+
+  static constexpr uint16_t be16 = 0x3412;
+  static constexpr uint32_t be32 = 0x78563412;
+  static constexpr uint64_t be64 = 0xf0debc9a78563412;
+
+  ASSERT_EQ(be16, htons(le16));
+  ASSERT_EQ(be32, htonl(le32));
+  ASSERT_EQ(be64, htonq(le64));
+
+  ASSERT_EQ(le16, ntohs(be16));
+  ASSERT_EQ(le32, ntohl(be32));
+  ASSERT_EQ(le64, ntohq(be64));
+
+  ASSERT_EQ(be16, htobe16(le16));
+  ASSERT_EQ(be32, htobe32(le32));
+  ASSERT_EQ(be64, htobe64(le64));
+
+  ASSERT_EQ(le16, betoh16(be16));
+  ASSERT_EQ(le32, betoh32(be32));
+  ASSERT_EQ(le64, betoh64(be64));
+
+  ASSERT_EQ(le16, htole16(le16));
+  ASSERT_EQ(le32, htole32(le32));
+  ASSERT_EQ(le64, htole64(le64));
+
+  ASSERT_EQ(le16, letoh16(le16));
+  ASSERT_EQ(le32, letoh32(le32));
+  ASSERT_EQ(le64, letoh64(le64));
+
+  ASSERT_EQ(le16, be16toh(be16));
+  ASSERT_EQ(le32, be32toh(be32));
+  ASSERT_EQ(le64, be64toh(be64));
+
+  ASSERT_EQ(le16, le16toh(le16));
+  ASSERT_EQ(le32, le32toh(le32));
+  ASSERT_EQ(le64, le64toh(le64));
+}
diff --git a/base/file.cpp b/base/file.cpp
index 6284b04..81b04d7 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -18,11 +18,13 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <libgen.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include <memory>
+#include <mutex>
 #include <string>
 #include <vector>
 
@@ -236,5 +238,59 @@
 #endif
 }
 
+std::string GetExecutableDirectory() {
+  return Dirname(GetExecutablePath());
+}
+
+std::string Basename(const std::string& path) {
+  // Copy path because basename may modify the string passed in.
+  std::string result(path);
+
+#if !defined(__BIONIC__)
+  // Use lock because basename() may write to a process global and return a
+  // pointer to that. Note that this locking strategy only works if all other
+  // callers to basename in the process also grab this same lock, but its
+  // better than nothing.  Bionic's basename returns a thread-local buffer.
+  static std::mutex& basename_lock = *new std::mutex();
+  std::lock_guard<std::mutex> lock(basename_lock);
+#endif
+
+  // Note that if std::string uses copy-on-write strings, &str[0] will cause
+  // the copy to be made, so there is no chance of us accidentally writing to
+  // the storage for 'path'.
+  char* name = basename(&result[0]);
+
+  // In case basename returned a pointer to a process global, copy that string
+  // before leaving the lock.
+  result.assign(name);
+
+  return result;
+}
+
+std::string Dirname(const std::string& path) {
+  // Copy path because dirname may modify the string passed in.
+  std::string result(path);
+
+#if !defined(__BIONIC__)
+  // Use lock because dirname() may write to a process global and return a
+  // pointer to that. Note that this locking strategy only works if all other
+  // callers to dirname in the process also grab this same lock, but its
+  // better than nothing.  Bionic's dirname returns a thread-local buffer.
+  static std::mutex& dirname_lock = *new std::mutex();
+  std::lock_guard<std::mutex> lock(dirname_lock);
+#endif
+
+  // Note that if std::string uses copy-on-write strings, &str[0] will cause
+  // the copy to be made, so there is no chance of us accidentally writing to
+  // the storage for 'path'.
+  char* parent = dirname(&result[0]);
+
+  // In case dirname returned a pointer to a process global, copy that string
+  // before leaving the lock.
+  result.assign(parent);
+
+  return result;
+}
+
 }  // namespace base
 }  // namespace android
diff --git a/base/file_test.cpp b/base/file_test.cpp
index f741d89..1021326 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -134,7 +134,11 @@
   // Linux doesn't allow empty symbolic links.
   std::string min("x");
   // ext2 and ext4 both have PAGE_SIZE limits.
-  std::string max(static_cast<size_t>(4096 - 1), 'x');
+  // If file encryption is enabled, there's extra overhead to store the
+  // size of the encrypted symlink target. There's also an off-by-one
+  // in current kernels (and marlin/sailfish where we're seeing this
+  // failure are still on 3.18, far from current). http://b/33306057.
+  std::string max(static_cast<size_t>(4096 - 2 - 1 - 1), 'x');
 
   TemporaryDir td;
   std::string min_path{std::string(td.path) + "/" + "min"};
@@ -155,6 +159,26 @@
 #endif
 }
 
+TEST(file, GetExecutableDirectory) {
+  std::string path = android::base::GetExecutableDirectory();
+  ASSERT_NE("", path);
+  ASSERT_NE(android::base::GetExecutablePath(), path);
+  ASSERT_EQ('/', path[0]);
+  ASSERT_NE('/', path[path.size() - 1]);
+}
+
 TEST(file, GetExecutablePath) {
   ASSERT_NE("", android::base::GetExecutablePath());
 }
+
+TEST(file, Basename) {
+  EXPECT_EQ("sh", android::base::Basename("/system/bin/sh"));
+  EXPECT_EQ("sh", android::base::Basename("sh"));
+  EXPECT_EQ("sh", android::base::Basename("/system/bin/sh/"));
+}
+
+TEST(file, Dirname) {
+  EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh"));
+  EXPECT_EQ(".", android::base::Dirname("sh"));
+  EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh/"));
+}
diff --git a/base/include/android-base/endian.h b/base/include/android-base/endian.h
new file mode 100644
index 0000000..6eb677c
--- /dev/null
+++ b/base/include/android-base/endian.h
@@ -0,0 +1,90 @@
+/*
+ * 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_ENDIAN_H
+#define ANDROID_BASE_ENDIAN_H
+
+/* A cross-platform equivalent of bionic's <sys/endian.h>. */
+
+#if defined(__BIONIC__)
+
+#include <sys/endian.h>
+
+#elif defined(__GLIBC__)
+
+/* glibc's <endian.h> is like bionic's <sys/endian.h>. */
+#include <endian.h>
+
+/* glibc keeps htons and htonl in <netinet/in.h>. */
+#include <netinet/in.h>
+
+/* glibc doesn't have the 64-bit variants. */
+#define htonq(x) htobe64(x)
+#define ntohq(x) be64toh(x)
+
+/* glibc has different names to BSD for these. */
+#define betoh16(x) be16toh(x)
+#define betoh32(x) be32toh(x)
+#define betoh64(x) be64toh(x)
+
+#else
+
+/* Mac OS and Windows have nothing. */
+
+#define __LITTLE_ENDIAN 1234
+#define LITTLE_ENDIAN __LITTLE_ENDIAN
+
+#define __BIG_ENDIAN 4321
+#define BIG_ENDIAN __BIG_ENDIAN
+
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#define BYTE_ORDER __BYTE_ORDER
+
+#define htons(x) __builtin_bswap16(x)
+#define htonl(x) __builtin_bswap32(x)
+#define htonq(x) __builtin_bswap64(x)
+
+#define ntohs(x) __builtin_bswap16(x)
+#define ntohl(x) __builtin_bswap32(x)
+#define ntohq(x) __builtin_bswap64(x)
+
+#define htobe16(x) __builtin_bswap16(x)
+#define htobe32(x) __builtin_bswap32(x)
+#define htobe64(x) __builtin_bswap64(x)
+
+#define betoh16(x) __builtin_bswap16(x)
+#define betoh32(x) __builtin_bswap32(x)
+#define betoh64(x) __builtin_bswap64(x)
+
+#define htole16(x) (x)
+#define htole32(x) (x)
+#define htole64(x) (x)
+
+#define letoh16(x) (x)
+#define letoh32(x) (x)
+#define letoh64(x) (x)
+
+#define be16toh(x) __builtin_bswap16(x)
+#define be32toh(x) __builtin_bswap32(x)
+#define be64toh(x) __builtin_bswap64(x)
+
+#define le16toh(x) (x)
+#define le32toh(x) (x)
+#define le64toh(x) (x)
+
+#endif
+
+#endif  // ANDROID_BASE_ENDIAN_H
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index cd64262..33d1ab3 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -51,6 +51,12 @@
 #endif
 
 std::string GetExecutablePath();
+std::string GetExecutableDirectory();
+
+// Like the regular basename and dirname, but thread-safe on all
+// platforms and capable of correctly handling exotic Windows paths.
+std::string Basename(const std::string& path);
+std::string Dirname(const std::string& path);
 
 }  // namespace base
 }  // namespace android
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index 50677a3..e78edbb 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -17,6 +17,37 @@
 #ifndef ANDROID_BASE_LOGGING_H
 #define ANDROID_BASE_LOGGING_H
 
+//
+// Google-style C++ logging.
+//
+
+// This header provides a C++ stream interface to logging.
+//
+// To log:
+//
+//   LOG(INFO) << "Some text; " << some_value;
+//
+// Replace `INFO` with any severity from `enum LogSeverity`.
+//
+// To log the result of a failed function and include the string
+// representation of `errno` at the end:
+//
+//   PLOG(ERROR) << "Write failed";
+//
+// The output will be something like `Write failed: I/O error`.
+// Remember this as 'P' as in perror(3).
+//
+// To output your own types, simply implement operator<< as normal.
+//
+// By default, output goes to logcat on Android and stderr on the host.
+// A process can use `SetLogger` to decide where all logging goes.
+// Implementations are provided for logcat, stderr, and dmesg.
+
+// This header also provides assertions:
+//
+//   CHECK(must_be_true);
+//   CHECK_EQ(a, b) << z_is_interesting_too;
+
 // NOTE: For Windows, you must include logging.h after windows.h to allow the
 // following code to suppress the evil ERROR macro:
 #ifdef _WIN32
diff --git a/base/include/android-base/memory.h b/base/include/android-base/memory.h
index 3a2f8fa..9971226 100644
--- a/base/include/android-base/memory.h
+++ b/base/include/android-base/memory.h
@@ -20,25 +20,19 @@
 namespace android {
 namespace base {
 
-// Use packed structures for access to unaligned data on targets with alignment
+// Use memcpy for access to unaligned data on targets with alignment
 // restrictions.  The compiler will generate appropriate code to access these
 // structures without generating alignment exceptions.
 template <typename T>
-static inline T get_unaligned(const T* address) {
-  struct unaligned {
-    T v;
-  } __attribute__((packed));
-  const unaligned* p = reinterpret_cast<const unaligned*>(address);
-  return p->v;
+static inline T get_unaligned(const void* address) {
+  T result;
+  memcpy(&result, address, sizeof(T));
+  return result;
 }
 
 template <typename T>
-static inline void put_unaligned(T* address, T v) {
-  struct unaligned {
-    T v;
-  } __attribute__((packed));
-  unaligned* p = reinterpret_cast<unaligned*>(address);
-  p->v = v;
+static inline void put_unaligned(void* address, T v) {
+  memcpy(address, &v, sizeof(T));
 }
 
 } // namespace base
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 95d1b6a..4de5e57 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -23,6 +23,7 @@
 #error Only bionic supports system properties.
 #endif
 
+#include <chrono>
 #include <limits>
 #include <string>
 
@@ -58,7 +59,20 @@
 // tell you whether or not your call succeeded. A `false` return value definitely means failure.
 bool SetProperty(const std::string& key, const std::string& value);
 
+// 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);
+
+// 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);
+
 } // namespace base
 } // namespace android
 
-#endif  // ANDROID_BASE_MEMORY_H
+#endif  // ANDROID_BASE_PROPERTIES_H
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index b8a9289..f5f5c11 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -64,6 +64,9 @@
 bool EndsWith(const std::string& s, const char* suffix);
 bool EndsWithIgnoreCase(const std::string& s, const char* suffix);
 
+// Tests whether 'lhs' equals 'rhs', ignoring case.
+bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs);
+
 }  // namespace base
 }  // namespace android
 
diff --git a/base/logging.cpp b/base/logging.cpp
index cbc3c8a..6357b4b 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -118,27 +118,32 @@
 namespace android {
 namespace base {
 
-static auto& logging_lock = *new std::mutex();
+static std::mutex& LoggingLock() {
+  static auto& logging_lock = *new std::mutex();
+  return logging_lock;
+}
 
+static LogFunction& Logger() {
 #ifdef __ANDROID__
-static auto& gLogger = *new LogFunction(LogdLogger());
+  static auto& logger = *new LogFunction(LogdLogger());
 #else
-static auto& gLogger = *new LogFunction(StderrLogger);
+  static auto& logger = *new LogFunction(StderrLogger);
 #endif
+  return logger;
+}
 
-static auto& gAborter = *new AbortFunction(DefaultAborter);
+static AbortFunction& Aborter() {
+  static auto& aborter = *new AbortFunction(DefaultAborter);
+  return aborter;
+}
+
+static std::string& ProgramInvocationName() {
+  static auto& programInvocationName = *new std::string(getprogname());
+  return programInvocationName;
+}
 
 static bool gInitialized = false;
 static LogSeverity gMinimumLogSeverity = INFO;
-static auto& gProgramInvocationName = *new std::unique_ptr<std::string>();
-
-static const char* ProgramInvocationName() {
-  if (gProgramInvocationName == nullptr) {
-    gProgramInvocationName.reset(new std::string(getprogname()));
-  }
-
-  return gProgramInvocationName->c_str();
-}
 
 #if defined(__linux__)
 void KernelLogger(android::base::LogId, android::base::LogSeverity severity,
@@ -198,7 +203,7 @@
   static_assert(arraysize(log_characters) - 1 == FATAL + 1,
                 "Mismatch in size of log_characters and values in LogSeverity");
   char severity_char = log_characters[severity];
-  fprintf(stderr, "%s %c %s %5d %5d %s:%u] %s\n", ProgramInvocationName(),
+  fprintf(stderr, "%s %c %s %5d %5d %s:%u] %s\n", ProgramInvocationName().c_str(),
           severity_char, timestamp, getpid(), GetThreadId(), file, line, message);
 }
 
@@ -262,7 +267,8 @@
   // Linux to recover this, but we don't have that luxury on the Mac/Windows,
   // and there are a couple of argv[0] variants that are commonly used.
   if (argv != nullptr) {
-    gProgramInvocationName.reset(new std::string(basename(argv[0])));
+    std::lock_guard<std::mutex> lock(LoggingLock());
+    ProgramInvocationName() = basename(argv[0]);
   }
 
   const char* tags = getenv("ANDROID_LOG_TAGS");
@@ -307,13 +313,13 @@
 }
 
 void SetLogger(LogFunction&& logger) {
-  std::lock_guard<std::mutex> lock(logging_lock);
-  gLogger = std::move(logger);
+  std::lock_guard<std::mutex> lock(LoggingLock());
+  Logger() = std::move(logger);
 }
 
 void SetAborter(AbortFunction&& aborter) {
-  std::lock_guard<std::mutex> lock(logging_lock);
-  gAborter = std::move(aborter);
+  std::lock_guard<std::mutex> lock(LoggingLock());
+  Aborter() = std::move(aborter);
 }
 
 static const char* GetFileBasename(const char* file) {
@@ -403,7 +409,7 @@
 
   {
     // Do the actual logging with the lock held.
-    std::lock_guard<std::mutex> lock(logging_lock);
+    std::lock_guard<std::mutex> lock(LoggingLock());
     if (msg.find('\n') == std::string::npos) {
       LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
               data_->GetSeverity(), msg.c_str());
@@ -424,7 +430,7 @@
 
   // Abort if necessary.
   if (data_->GetSeverity() == FATAL) {
-    gAborter(msg.c_str());
+    Aborter()(msg.c_str());
   }
 }
 
@@ -434,8 +440,8 @@
 
 void LogMessage::LogLine(const char* file, unsigned int line, LogId id,
                          LogSeverity severity, const char* message) {
-  const char* tag = ProgramInvocationName();
-  gLogger(id, severity, tag, file, line, message);
+  const char* tag = ProgramInvocationName().c_str();
+  Logger()(id, severity, tag, file, line, message);
 }
 
 LogSeverity GetMinimumLogSeverity() {
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 2d9c2ba..adb041b 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -594,3 +594,7 @@
 
   EXPECT_EQ(CountLineAborter::newline_count, 1U + 1U);  // +1 for final '\n'.
 }
+
+__attribute__((constructor)) void TestLoggingInConstructor() {
+  LOG(ERROR) << "foobar";
+}
diff --git a/base/properties.cpp b/base/properties.cpp
index 37daf9a..32c0128 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -14,14 +14,21 @@
  * limitations under the License.
  */
 
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+
 #include "android-base/properties.h"
 
 #include <sys/system_properties.h>
+#include <sys/_system_properties.h>
 
+#include <algorithm>
+#include <chrono>
 #include <string>
 
 #include <android-base/parseint.h>
 
+using namespace std::chrono_literals;
+
 namespace android {
 namespace base {
 
@@ -78,5 +85,94 @@
   return (__system_property_set(key.c_str(), value.c_str()) == 0);
 }
 
+struct WaitForPropertyData {
+  bool done;
+  const std::string* expected_value;
+  unsigned last_read_serial;
+};
+
+static void WaitForPropertyCallback(void* data_ptr, const char*, const char* value, unsigned serial) {
+  WaitForPropertyData* data = reinterpret_cast<WaitForPropertyData*>(data_ptr);
+  if (*data->expected_value == value) {
+    data->done = true;
+  } else {
+    data->last_read_serial = serial;
+  }
+}
+
+// TODO: chrono_utils?
+static void DurationToTimeSpec(timespec& ts, std::chrono::nanoseconds 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();
+}
+
+using AbsTime = std::chrono::time_point<std::chrono::steady_clock>;
+
+static void UpdateTimeSpec(timespec& ts,
+                           const AbsTime& timeout) {
+  auto now = std::chrono::steady_clock::now();
+  auto remaining_timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(timeout - now);
+  if (remaining_timeout < 0ns) {
+    ts = { 0, 0 };
+  } else {
+    DurationToTimeSpec(ts, remaining_timeout);
+  }
+}
+
+// Waits for the system property `key` to be created.
+// Times out after `relative_timeout`.
+// Sets absolute_timeout which represents absolute time for the timeout.
+// 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;
+
+  // Find the property's prop_info*.
+  const prop_info* pi;
+  unsigned global_serial = 0;
+  while ((pi = __system_property_find(key.c_str())) == nullptr) {
+    // The property doesn't even exist yet.
+    // Wait for a global change and then look again.
+    timespec ts;
+    UpdateTimeSpec(ts, absolute_timeout);
+    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,
+                     std::chrono::milliseconds relative_timeout) {
+  AbsTime absolute_timeout;
+  const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, absolute_timeout);
+  if (pi == nullptr) return false;
+
+  WaitForPropertyData data;
+  data.expected_value = &expected_value;
+  data.done = false;
+  while (true) {
+    timespec ts;
+    // Check whether the property has the value we're looking for?
+    __system_property_read_callback(pi, WaitForPropertyCallback, &data);
+    if (data.done) return true;
+
+    // It didn't, so wait for the property to change before checking again.
+    UpdateTimeSpec(ts, absolute_timeout);
+    uint32_t unused;
+    if (!__system_property_wait(pi, data.last_read_serial, &unused, &ts)) return false;
+  }
+}
+
+bool WaitForPropertyCreation(const std::string& key,
+                             std::chrono::milliseconds relative_timeout) {
+  AbsTime absolute_timeout;
+  return (WaitForPropertyCreation(key, relative_timeout, absolute_timeout) != nullptr);
+}
+
 }  // namespace base
 }  // namespace android
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index da89ec5..1bbe482 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -18,7 +18,12 @@
 
 #include <gtest/gtest.h>
 
+#include <atomic>
+#include <chrono>
 #include <string>
+#include <thread>
+
+using namespace std::chrono_literals;
 
 TEST(properties, smoke) {
   android::base::SetProperty("debug.libbase.property_test", "hello");
@@ -119,3 +124,51 @@
 TEST(properties, GetUintProperty_uint16_t) { CheckGetUintProperty<uint16_t>(); }
 TEST(properties, GetUintProperty_uint32_t) { CheckGetUintProperty<uint32_t>(); }
 TEST(properties, GetUintProperty_uint64_t) { CheckGetUintProperty<uint64_t>(); }
+
+TEST(properties, WaitForProperty) {
+  std::atomic<bool> flag{false};
+  std::thread thread([&]() {
+    std::this_thread::sleep_for(100ms);
+    android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+    while (!flag) std::this_thread::yield();
+    android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+  });
+
+  ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
+  flag = true;
+  ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", 1s));
+  thread.join();
+}
+
+TEST(properties, WaitForProperty_timeout) {
+  auto t0 = std::chrono::steady_clock::now();
+  ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_timeout_test", "a",
+                                              200ms));
+  auto t1 = std::chrono::steady_clock::now();
+
+  ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
+  // Upper bounds on timing are inherently flaky, but let's try...
+  ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+}
+
+TEST(properties, WaitForPropertyCreation) {
+  std::thread thread([&]() {
+    std::this_thread::sleep_for(100ms);
+    android::base::SetProperty("debug.libbase.WaitForPropertyCreation_test", "a");
+  });
+
+  ASSERT_TRUE(android::base::WaitForPropertyCreation(
+          "debug.libbase.WaitForPropertyCreation_test", 1s));
+  thread.join();
+}
+
+TEST(properties, WaitForPropertyCreation_timeout) {
+  auto t0 = std::chrono::steady_clock::now();
+  ASSERT_FALSE(android::base::WaitForPropertyCreation(
+          "debug.libbase.WaitForPropertyCreation_timeout_test", 200ms));
+  auto t1 = std::chrono::steady_clock::now();
+
+  ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
+  // Upper bounds on timing are inherently flaky, but let's try...
+  ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+}
diff --git a/base/strings.cpp b/base/strings.cpp
index 7a94ad7..bfdaf12 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -36,11 +36,12 @@
 
   size_t base = 0;
   size_t found;
-  do {
+  while (true) {
     found = s.find_first_of(delimiters, base);
     result.push_back(s.substr(base, found - base));
+    if (found == s.npos) break;
     base = found + 1;
-  } while (found != s.npos);
+  }
 
   return result;
 }
@@ -112,5 +113,9 @@
   return EndsWith(s, suffix, false);
 }
 
+bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs) {
+  return strcasecmp(lhs.c_str(), rhs.c_str()) == 0;
+}
+
 }  // namespace base
 }  // namespace android
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 5fb21dd..7ed5b2b 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -244,3 +244,14 @@
   ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "OBA"));
   ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "FOO"));
 }
+
+TEST(strings, EqualsIgnoreCase) {
+  ASSERT_TRUE(android::base::EqualsIgnoreCase("foo", "FOO"));
+  ASSERT_TRUE(android::base::EqualsIgnoreCase("FOO", "foo"));
+  ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "bar"));
+  ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "fool"));
+}
+
+TEST(strings, ubsan_28729303) {
+  android::base::Split("/dev/null", ":");
+}
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 3b3d698..636477d 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -57,7 +57,13 @@
 
 static std::string GetSystemTempDir() {
 #if defined(__ANDROID__)
-  return "/data/local/tmp";
+  const char* tmpdir = "/data/local/tmp";
+  if (access(tmpdir, R_OK | W_OK | X_OK) == 0) {
+    return tmpdir;
+  }
+  // Tests running in app context can't access /data/local/tmp,
+  // so try current directory if /data/local/tmp is not accessible.
+  return ".";
 #elif defined(_WIN32)
   char tmp_dir[MAX_PATH];
   DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir);
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index d98a9d7..f744ad1 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -16,7 +16,6 @@
 
 bootstat_lib_src_files = [
     "boot_event_record_store.cpp",
-    "histogram_logger.cpp",
     "uptime_parser.cpp",
 ]
 
@@ -27,15 +26,12 @@
         "-Wall",
         "-Wextra",
         "-Werror",
-
-        // 524291 corresponds to sysui_histogram, from
-        // frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
-        "-DHISTOGRAM_LOG_TAG=524291",
     ],
     shared_libs: [
         "libbase",
         "libcutils",
         "liblog",
+        "libmetricslogger",
     ],
     whole_static_libs: ["libgtest_prod"],
     // Clang is required because of C++14
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 78be944..2648594 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -26,7 +26,6 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
-#include "histogram_logger.h"
 #include "uptime_parser.h"
 
 namespace {
@@ -46,24 +45,6 @@
 
   *uptime = file_stat.st_mtime;
 
-  // The following code (till function exit) is a debug test to ensure the
-  // validity of the file mtime value, i.e., to check that the record file
-  // mtime values are not changed once set.
-  // TODO(jhawkins): Remove this code.
-  std::string content;
-  if (!android::base::ReadFileToString(path, &content)) {
-    PLOG(ERROR) << "Failed to read " << path;
-    return false;
-  }
-
-  // Ignore existing bootstat records (which do not contain file content).
-  if (!content.empty()) {
-    int32_t value;
-    if (android::base::ParseInt(content, &value)) {
-      bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
-    }
-  }
-
   return true;
 }
 
@@ -89,16 +70,6 @@
     return;
   }
 
-  // Writing the value as content in the record file is a debug measure to
-  // ensure the validity of the file mtime value, i.e., to check that the record
-  // file mtime values are not changed once set.
-  // TODO(jhawkins): Remove this block.
-  if (!android::base::WriteStringToFd(std::to_string(value), record_fd)) {
-    PLOG(ERROR) << "Failed to write value to " << record_path;
-    close(record_fd);
-    return;
-  }
-
   // Fill out the stat structure for |record_path| in order to get the atime to
   // set in the utime() call.
   struct stat file_stat;
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 01c2cc1..90f6513 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -45,14 +45,6 @@
     return false;
   }
 
-  // Writing the value as content in the record file is a debug measure to
-  // ensure the validity of the file mtime value, i.e., to check that the record
-  // file mtime values are not changed once set.
-  // TODO(jhawkins): Remove this block.
-  if (!android::base::WriteStringToFd(std::to_string(value), record_fd)) {
-    return false;
-  }
-
   // Set the |mtime| of the file to store the value of the boot event while
   // preserving the |atime|.
   struct timeval atime = {/* tv_sec */ 0, /* tv_usec */ 0};
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 7c0e85d..a4cc5f2 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -28,14 +28,16 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <vector>
 
 #include <android/log.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
+#include <android-base/strings.h>
 #include <cutils/properties.h>
+#include <metricslogger/metrics_logger.h>
 
 #include "boot_event_record_store.h"
-#include "histogram_logger.h"
 #include "uptime_parser.h"
 
 namespace {
@@ -47,7 +49,7 @@
 
   auto events = boot_event_store.GetAllBootEvents();
   for (auto i = events.cbegin(); i != events.cend(); ++i) {
-    bootstat::LogHistogram(i->first, i->second);
+    android::metricslogger::LogHistogram(i->first, i->second);
   }
 }
 
@@ -207,6 +209,46 @@
   return boot_complete_prefix;
 }
 
+// Records the value of a given ro.boottime.init property in milliseconds.
+void RecordInitBootTimeProp(
+    BootEventRecordStore* boot_event_store, const char* property) {
+  std::string value = GetProperty(property);
+
+  int32_t time_in_ms;
+  if (android::base::ParseInt(value, &time_in_ms)) {
+    boot_event_store->AddBootEventWithValue(property, time_in_ms);
+  }
+}
+
+// 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'.
+  std::string value = GetProperty("ro.boot.boottime");
+  if (value.empty()) {
+    // ro.boot.boottime is not reported on all devices.
+    return;
+  }
+
+  int32_t total_time = 0;
+  auto stages = android::base::Split(value, ",");
+  for (auto const &stageTiming : stages) {
+    // |stageTiming| is of the form 'stage:time'.
+    auto stageTimingValues = android::base::Split(stageTiming, ":");
+    DCHECK_EQ(2, 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);
+    }
+  }
+
+  boot_event_store->AddBootEventWithValue("boottime.bootloader.total", total_time);
+}
+
 // 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() {
@@ -256,6 +298,12 @@
   // Record the total time from device startup to boot complete, regardless of
   // encryption state.
   boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime);
+
+  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);
 }
 
 // Records the boot_reason metric by querying the ro.boot.bootreason system
@@ -277,18 +325,18 @@
 
   if (current_time_utc < 0) {
     // UMA does not display negative values in buckets, so convert to positive.
-    bootstat::LogHistogram(
+    android::metricslogger::LogHistogram(
         "factory_reset_current_time_failure", std::abs(current_time_utc));
 
-    // Logging via BootEventRecordStore to see if using bootstat::LogHistogram
+    // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
     // is losing records somehow.
     boot_event_store.AddBootEventWithValue(
         "factory_reset_current_time_failure", std::abs(current_time_utc));
     return;
   } else {
-    bootstat::LogHistogram("factory_reset_current_time", current_time_utc);
+    android::metricslogger::LogHistogram("factory_reset_current_time", current_time_utc);
 
-    // Logging via BootEventRecordStore to see if using bootstat::LogHistogram
+    // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
     // is losing records somehow.
     boot_event_store.AddBootEventWithValue(
         "factory_reset_current_time", current_time_utc);
@@ -307,9 +355,9 @@
   // Calculate and record the difference in time between now and the
   // factory_reset time.
   time_t factory_reset_utc = record.second;
-  bootstat::LogHistogram("factory_reset_record_value", factory_reset_utc);
+  android::metricslogger::LogHistogram("factory_reset_record_value", factory_reset_utc);
 
-  // Logging via BootEventRecordStore to see if using bootstat::LogHistogram
+  // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
   // is losing records somehow.
   boot_event_store.AddBootEventWithValue(
       "factory_reset_record_value", factory_reset_utc);
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index c96e996..f4756d5 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -8,11 +8,30 @@
 #
 # post-fs-data: /data is writable
 # property:init.svc.bootanim=running: The boot animation is running
-on post-fs-data && property:init.svc.bootanim=running
+# property:ro.crypto.type=block: FDE device
+on post-fs-data && property:init.svc.bootanim=running && property:ro.crypto.type=block
     exec - root root -- /system/bin/bootstat -r post_decrypt_time_elapsed
 
+# sys.logbootcomplete is a signal to enable the bootstat logging mechanism.
+# This signaling is necessary to prevent logging boot metrics after a runtime
+# restart (e.g., adb shell stop && adb shell start).  /proc/uptime is not reset
+# during a runtime restart, which leads to false boot time metrics being reported.
+#
+# The 'on boot' event occurs once per hard boot (device power on), which
+# switches the flag on. If the device performs a runtime restart, the flag is
+# switched off and cannot be switched on until the device hard boots again.
+
+# Enable bootstat logging on boot.
+on boot
+    setprop sys.logbootcomplete 1
+
+# Disable further bootstat logging on a runtime restart. A runtime restart is
+# signaled by the zygote stopping.
+on property:init.svc.zygote=stopping
+    setprop sys.logbootcomplete 0
+
 # Record boot complete metrics.
-on property:sys.boot_completed=1
+on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
     # Record boot_complete and related stats (decryption, etc).
     exec - root root -- /system/bin/bootstat --record_boot_complete
 
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index f0131b8..8d2ea68 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -1,25 +1,241 @@
-cc_library_static {
-  name: "libdebuggerd_client",
-  srcs: ["client/debuggerd_client.cpp"],
+cc_defaults {
+    name: "debuggerd_defaults",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wno-error",
+        "-Wno-nullability-completeness",
+        "-Os",
+    ],
 
-  cflags: [
-    "-Wall",
-    "-Wextra",
-    "-Werror",
-    "-Os",
-  ],
-
-  target: {
-    android64: {
-      cflags: ["-DTARGET_IS_64_BIT"],
-    },
-  },
-
-  local_include_dirs: ["include"],
-  export_include_dirs: ["include"],
-
-  // libdebuggerd_client gets async signal safe logging via libc_logging,
-  // which defines its interface in bionic private headers.
-  include_dirs: ["bionic/libc"],
-  static_libs: ["libc_logging"],
+    local_include_dirs: ["include"],
 }
+
+cc_library_static {
+    name: "libdebuggerd_handler_core",
+    defaults: ["debuggerd_defaults"],
+    srcs: ["handler/debuggerd_handler.cpp"],
+
+    // libdebuggerd_handler gets async signal safe logging via libc_logging,
+    // which defines its interface in bionic private headers.
+    include_dirs: ["bionic/libc"],
+    whole_static_libs: [
+        "libc_logging",
+        "libdebuggerd",
+    ],
+
+    export_include_dirs: ["include"],
+}
+
+cc_library_static {
+    name: "libdebuggerd_handler",
+    defaults: ["debuggerd_defaults"],
+    srcs: ["handler/debuggerd_fallback_nop.cpp"],
+
+    whole_static_libs: [
+        "libdebuggerd_handler_core",
+    ],
+
+    export_include_dirs: ["include"],
+}
+
+cc_library_static {
+    name: "libdebuggerd_handler_fallback",
+    defaults: ["debuggerd_defaults"],
+    srcs: ["handler/debuggerd_fallback.cpp"],
+
+    // libdebuggerd_handler gets async signal safe logging via libc_logging,
+    // which defines its interface in bionic private headers.
+    include_dirs: ["bionic/libc"],
+    static_libs: [
+        "libdebuggerd",
+        "libbacktrace",
+        "libunwind",
+        "liblzma",
+        "libcutils",
+    ],
+
+    export_include_dirs: ["include"],
+}
+
+cc_library {
+    name: "libdebuggerd_client",
+    defaults: ["debuggerd_defaults"],
+    srcs: [
+        "client/debuggerd_client.cpp",
+        "util.cpp",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+    ],
+    export_include_dirs: ["include"],
+}
+
+cc_library_static {
+    name: "libdebuggerd",
+    defaults: ["debuggerd_defaults"],
+
+    srcs: [
+        "libdebuggerd/backtrace.cpp",
+        "libdebuggerd/elf_utils.cpp",
+        "libdebuggerd/open_files_list.cpp",
+        "libdebuggerd/tombstone.cpp",
+        "libdebuggerd/utility.cpp",
+    ],
+
+    target: {
+        android_arm: {
+            srcs: ["libdebuggerd/arm/machine.cpp"],
+        },
+        android_arm64: {
+            srcs: ["libdebuggerd/arm64/machine.cpp"],
+        },
+        android_mips: {
+            srcs: ["libdebuggerd/mips/machine.cpp"],
+        },
+        android_mips64: {
+            srcs: ["libdebuggerd/mips64/machine.cpp"],
+        },
+        android_x86: {
+            srcs: ["libdebuggerd/x86/machine.cpp"],
+        },
+        android_x86_64: {
+            srcs: ["libdebuggerd/x86_64/machine.cpp"],
+        },
+    },
+
+    local_include_dirs: ["libdebuggerd/include"],
+    export_include_dirs: ["libdebuggerd/include"],
+
+    static_libs: [
+        "libbacktrace",
+        "libunwind",
+        "liblzma",
+        "libbase",
+        "libcutils",
+        "liblog",
+    ],
+}
+
+cc_test {
+    name: "debuggerd_test",
+    defaults: ["debuggerd_defaults"],
+
+    cflags: ["-Wno-missing-field-initializers"],
+    srcs: [
+        "libdebuggerd/test/dump_memory_test.cpp",
+        "libdebuggerd/test/elf_fake.cpp",
+        "libdebuggerd/test/log_fake.cpp",
+        "libdebuggerd/test/open_files_list_test.cpp",
+        "libdebuggerd/test/property_fake.cpp",
+        "libdebuggerd/test/ptrace_fake.cpp",
+        "libdebuggerd/test/tombstone_test.cpp",
+    ],
+
+    target: {
+        android: {
+            srcs: [
+                "debuggerd_test.cpp",
+                "util.cpp"
+            ],
+        },
+    },
+
+    shared_libs: [
+        "libbacktrace",
+        "libbase",
+        "libcutils",
+    ],
+
+    static_libs: [
+        "libdebuggerd"
+    ],
+
+    local_include_dirs: [
+        "libdebuggerd",
+    ],
+
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            stem: "debuggerd_test32",
+        },
+        lib64: {
+            stem: "debuggerd_test64",
+        },
+    },
+}
+
+cc_binary {
+    name: "crash_dump",
+    srcs: [
+        "crash_dump.cpp",
+        "util.cpp",
+    ],
+    defaults: ["debuggerd_defaults"],
+
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+
+    static_libs: [
+        "libdebuggerd",
+        "libcutils",
+    ],
+
+    shared_libs: [
+        "libbacktrace",
+        "libbase",
+        "liblog",
+        "libprocinfo",
+        "libselinux",
+    ],
+}
+
+cc_binary {
+    name: "debuggerd",
+    srcs: [
+        "debuggerd.cpp",
+    ],
+    defaults: ["debuggerd_defaults"],
+
+    shared_libs: [
+        "libbase",
+        "libdebuggerd_client",
+        "liblog",
+        "libselinux",
+    ],
+
+    local_include_dirs: ["include"],
+}
+
+cc_binary {
+    name: "tombstoned",
+    srcs: [
+        "util.cpp",
+        "tombstoned/intercept_manager.cpp",
+        "tombstoned/tombstoned.cpp",
+    ],
+    defaults: ["debuggerd_defaults"],
+
+    static_libs: [
+        "libbase",
+        "libcutils",
+        "libevent",
+        "liblog",
+    ],
+
+    init_rc: ["tombstoned/tombstoned.rc"]
+}
+
+subdirs = [
+    "crasher",
+]
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
deleted file mode 100644
index 607745d..0000000
--- a/debuggerd/Android.mk
+++ /dev/null
@@ -1,168 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-common_cppflags := \
-    -W \
-    -Wall \
-    -Wextra \
-    -Wunused \
-    -Werror \
-
-ifeq ($(TARGET_IS_64_BIT),true)
-common_cppflags += -DTARGET_IS_64_BIT
-endif
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    backtrace.cpp \
-    debuggerd.cpp \
-    elf_utils.cpp \
-    getevent.cpp \
-    open_files_list.cpp \
-    signal_sender.cpp \
-    tombstone.cpp \
-    utility.cpp \
-
-LOCAL_SRC_FILES_arm    := arm/machine.cpp
-LOCAL_SRC_FILES_arm64  := arm64/machine.cpp
-LOCAL_SRC_FILES_mips   := mips/machine.cpp
-LOCAL_SRC_FILES_mips64 := mips64/machine.cpp
-LOCAL_SRC_FILES_x86    := x86/machine.cpp
-LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp
-
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CPPFLAGS := $(common_cppflags)
-
-LOCAL_INIT_RC_32 := debuggerd.rc
-LOCAL_INIT_RC_64 := debuggerd64.rc
-
-LOCAL_SHARED_LIBRARIES := \
-    libbacktrace \
-    libbase \
-    libcutils \
-    liblog \
-    libselinux \
-
-LOCAL_CLANG := true
-
-LOCAL_MODULE := debuggerd
-LOCAL_MODULE_STEM_32 := debuggerd
-LOCAL_MODULE_STEM_64 := debuggerd64
-LOCAL_MULTILIB := both
-
-include $(BUILD_EXECUTABLE)
-
-crasher_cppflags := $(common_cppflags) -fstack-protector-all -Wno-free-nonheap-object -Wno-date-time
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := crasher.cpp
-LOCAL_SRC_FILES_arm    := arm/crashglue.S
-LOCAL_SRC_FILES_arm64  := arm64/crashglue.S
-LOCAL_SRC_FILES_mips   := mips/crashglue.S
-LOCAL_SRC_FILES_mips64 := mips64/crashglue.S
-LOCAL_SRC_FILES_x86    := x86/crashglue.S
-LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := optional
-LOCAL_CPPFLAGS := $(crasher_cppflags)
-LOCAL_SHARED_LIBRARIES := libcutils liblog
-
-# The arm emulator has VFP but not VFPv3-D32.
-ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
-LOCAL_ASFLAGS_arm += -DHAS_VFP_D32
-endif
-
-LOCAL_MODULE := crasher
-LOCAL_MODULE_STEM_32 := crasher
-LOCAL_MODULE_STEM_64 := crasher64
-LOCAL_MULTILIB := both
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := crasher.cpp
-LOCAL_SRC_FILES_arm    := arm/crashglue.S
-LOCAL_SRC_FILES_arm64  := arm64/crashglue.S
-LOCAL_SRC_FILES_mips   := mips/crashglue.S
-LOCAL_SRC_FILES_mips64 := mips64/crashglue.S
-LOCAL_SRC_FILES_x86    := x86/crashglue.S
-LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := optional
-LOCAL_CPPFLAGS := $(crasher_cppflags) -DSTATIC_CRASHER
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_SHARED_LIBRARIES := libcutils liblog
-
-# The arm emulator has VFP but not VFPv3-D32.
-ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
-LOCAL_ASFLAGS_arm += -DHAS_VFP_D32
-endif
-
-LOCAL_MODULE := static_crasher
-LOCAL_MODULE_STEM_32 := static_crasher
-LOCAL_MODULE_STEM_64 := static_crasher64
-LOCAL_MULTILIB := both
-
-LOCAL_STATIC_LIBRARIES := libdebuggerd_client libcutils liblog
-
-include $(BUILD_EXECUTABLE)
-
-debuggerd_test_src_files := \
-    utility.cpp \
-    open_files_list.cpp \
-    test/dump_memory_test.cpp \
-    test/elf_fake.cpp \
-    test/log_fake.cpp \
-    test/open_files_list_test.cpp \
-    test/property_fake.cpp \
-    test/ptrace_fake.cpp \
-    test/tombstone_test.cpp \
-    test/selinux_fake.cpp \
-
-debuggerd_shared_libraries := \
-    libbacktrace \
-    libbase \
-    libcutils \
-    liblog
-
-debuggerd_c_includes := \
-    $(LOCAL_PATH)/test \
-
-debuggerd_cpp_flags := \
-    $(common_cppflags) \
-    -Wno-missing-field-initializers \
-    -fno-rtti \
-
-# Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
-debuggerd_cpp_flags += -Wno-varargs
-
-# Only build the host tests on linux.
-ifeq ($(HOST_OS),linux)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := debuggerd_test
-LOCAL_SRC_FILES := $(debuggerd_test_src_files)
-LOCAL_SHARED_LIBRARIES := $(debuggerd_shared_libraries)
-LOCAL_C_INCLUDES := $(debuggerd_c_includes)
-LOCAL_CPPFLAGS := $(debuggerd_cpp_flags)
-
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_MULTILIB := both
-include $(BUILD_HOST_NATIVE_TEST)
-
-endif
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := debuggerd_test
-LOCAL_SRC_FILES := $(debuggerd_test_src_files)
-LOCAL_SHARED_LIBRARIES := $(debuggerd_shared_libraries)
-LOCAL_C_INCLUDES := $(debuggerd_c_includes)
-LOCAL_CPPFLAGS := $(debuggerd_cpp_flags)
-
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_MULTILIB := both
-include $(BUILD_NATIVE_TEST)
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index c67d747..f2975d1 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -1,379 +1,148 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
+ * Copyright 2016, The Android Open Source Project
  *
- * 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.
+ * 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
  *
- * 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.
+ *     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 "debuggerd/client.h"
+#include <debuggerd/client.h>
 
-#include <errno.h>
-#include <inttypes.h>
-#include <pthread.h>
-#include <sched.h>
+#include <fcntl.h>
 #include <signal.h>
-#include <stddef.h>
-#include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <sys/socket.h>
-#include <sys/syscall.h>
-#include <sys/un.h>
-#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <unistd.h>
 
-#include "private/libc_logging.h"
+#include <chrono>
 
-#if defined(TARGET_IS_64_BIT) && !defined(__LP64__)
-#define SOCKET_NAME "android:debuggerd32"
-#else
-#define SOCKET_NAME "android:debuggerd"
-#endif
+#include <android-base/logging.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>
 
-// see man(2) prctl, specifically the section about PR_GET_NAME
-#define MAX_TASK_NAME_LEN (16)
+using android::base::unique_fd;
 
-static debuggerd_callbacks_t g_callbacks;
-
-// Don't use __libc_fatal because it exits via abort, which might put us back into a signal handler.
-#define fatal(...)                                             \
-  do {                                                         \
-    __libc_format_log(ANDROID_LOG_FATAL, "libc", __VA_ARGS__); \
-    _exit(1);                                                  \
-  } while (0)
-
-static int socket_abstract_client(const char* name, int type) {
-  sockaddr_un addr;
-
-  // Test with length +1 for the *initial* '\0'.
-  size_t namelen = strlen(name);
-  if ((namelen + 1) > sizeof(addr.sun_path)) {
-    errno = EINVAL;
-    return -1;
-  }
-
-  // This is used for abstract socket namespace, we need
-  // an initial '\0' at the start of the Unix socket path.
-  //
-  // Note: The path in this case is *not* supposed to be
-  // '\0'-terminated. ("man 7 unix" for the gory details.)
-  memset(&addr, 0, sizeof(addr));
-  addr.sun_family = AF_LOCAL;
-  addr.sun_path[0] = 0;
-  memcpy(addr.sun_path + 1, name, namelen);
-
-  socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
-
-  int s = socket(AF_LOCAL, type, 0);
-  if (s == -1) {
-    return -1;
-  }
-
-  int rc = TEMP_FAILURE_RETRY(connect(s, reinterpret_cast<sockaddr*>(&addr), alen));
-  if (rc == -1) {
-    close(s);
-    return -1;
-  }
-
-  return s;
-}
-
-/*
- * 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
- * indication of the failure in the log.
- *
- * We could be here as a result of native heap corruption, or while a
- * mutex is being held, so we don't want to use any libc functions that
- * could allocate memory or hold a lock.
- */
-static void log_signal_summary(int signum, const siginfo_t* info) {
-  const char* signal_name = "???";
-  bool has_address = false;
-  switch (signum) {
-    case SIGABRT:
-      signal_name = "SIGABRT";
-      break;
-    case SIGBUS:
-      signal_name = "SIGBUS";
-      has_address = true;
-      break;
-    case SIGFPE:
-      signal_name = "SIGFPE";
-      has_address = true;
-      break;
-    case SIGILL:
-      signal_name = "SIGILL";
-      has_address = true;
-      break;
-    case SIGSEGV:
-      signal_name = "SIGSEGV";
-      has_address = true;
-      break;
-#if defined(SIGSTKFLT)
-    case SIGSTKFLT:
-      signal_name = "SIGSTKFLT";
-      break;
-#endif
-    case SIGSYS:
-      signal_name = "SIGSYS";
-      break;
-    case SIGTRAP:
-      signal_name = "SIGTRAP";
-      break;
-  }
-
-  char thread_name[MAX_TASK_NAME_LEN + 1];  // one more for termination
-  if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
-    strcpy(thread_name, "<name unknown>");
-  } else {
-    // short names are null terminated by prctl, but the man page
-    // implies that 16 byte names are not.
-    thread_name[MAX_TASK_NAME_LEN] = 0;
-  }
-
-  // "info" will be null if the siginfo_t information was not available.
-  // Many signals don't have an address or a code.
-  char code_desc[32];  // ", code -6"
-  char addr_desc[32];  // ", fault addr 0x1234"
-  addr_desc[0] = code_desc[0] = 0;
-  if (info != nullptr) {
-    // For a rethrown signal, this si_code will be right and the one debuggerd shows will
-    // always be SI_TKILL.
-    __libc_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);
-    }
-  }
-  __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);
-}
-
-/*
- * Returns true if the handler for signal "signum" has SA_SIGINFO set.
- */
-static bool have_siginfo(int signum) {
-  struct sigaction old_action, new_action;
-
-  memset(&new_action, 0, sizeof(new_action));
-  new_action.sa_handler = SIG_DFL;
-  new_action.sa_flags = SA_RESTART;
-  sigemptyset(&new_action.sa_mask);
-
-  if (sigaction(signum, &new_action, &old_action) < 0) {
-    __libc_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s",
-                      strerror(errno));
+static bool send_signal(pid_t pid, bool backtrace) {
+  sigval val;
+  val.sival_int = backtrace;
+  if (sigqueue(pid, DEBUGGER_SIGNAL, val) != 0) {
+    PLOG(ERROR) << "libdebuggerd_client: failed to send signal to pid " << pid;
     return false;
   }
-  bool result = (old_action.sa_flags & SA_SIGINFO) != 0;
-
-  if (sigaction(signum, &old_action, nullptr) == -1) {
-    __libc_format_log(ANDROID_LOG_WARN, "libc", "Restore failed in test for SA_SIGINFO: %s",
-                      strerror(errno));
-  }
-  return result;
+  return true;
 }
 
-static void send_debuggerd_packet(pid_t crashing_tid, pid_t pseudothread_tid) {
-  // Mutex to prevent multiple crashing threads from trying to talk
-  // to debuggerd at the same time.
-  static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
-  int ret = pthread_mutex_trylock(&crash_mutex);
-  if (ret != 0) {
-    if (ret == EBUSY) {
-      __libc_format_log(ANDROID_LOG_INFO, "libc",
-                        "Another thread contacted debuggerd first; not contacting debuggerd.");
-      // This will never complete since the lock is never released.
-      pthread_mutex_lock(&crash_mutex);
-    } else {
-      __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_trylock failed: %s", strerror(ret));
+bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type,
+                            int timeout_ms) {
+  LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
+  unique_fd sockfd;
+  const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
+  auto set_timeout = [timeout_ms, &sockfd, &end]() {
+    if (timeout_ms <= 0) {
+      return true;
     }
-    return;
-  }
 
-  int s = socket_abstract_client(SOCKET_NAME, SOCK_STREAM | SOCK_CLOEXEC);
-  if (s == -1) {
-    __libc_format_log(ANDROID_LOG_FATAL, "libc", "Unable to open connection to debuggerd: %s",
-                      strerror(errno));
-    return;
-  }
-
-  // debuggerd knows our pid from the credentials on the
-  // local socket but we need to tell it the tid of the crashing thread.
-  // debuggerd will be paranoid and verify that we sent a tid
-  // that's actually in our process.
-  debugger_msg_t msg;
-  msg.action = DEBUGGER_ACTION_CRASH;
-  msg.tid = crashing_tid;
-  msg.ignore_tid = pseudothread_tid;
-  msg.abort_msg_address = 0;
-
-  if (g_callbacks.get_abort_message) {
-    msg.abort_msg_address = reinterpret_cast<uintptr_t>(g_callbacks.get_abort_message());
-  }
-
-  ret = TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg)));
-  if (ret == sizeof(msg)) {
-    char debuggerd_ack;
-    ret = TEMP_FAILURE_RETRY(read(s, &debuggerd_ack, 1));
-    if (g_callbacks.post_dump) {
-      g_callbacks.post_dump();
+    auto now = std::chrono::steady_clock::now();
+    if (now > end) {
+      return false;
     }
-  } else {
-    // read or write failed -- broken connection?
-    __libc_format_log(ANDROID_LOG_FATAL, "libc", "Failed while talking to debuggerd: %s",
-                      strerror(errno));
-  }
 
-  close(s);
-}
+    auto time_left = std::chrono::duration_cast<std::chrono::microseconds>(end - now);
+    auto seconds = std::chrono::duration_cast<std::chrono::seconds>(time_left);
+    auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(time_left - seconds);
+    struct timeval timeout = {
+      .tv_sec = static_cast<long>(seconds.count()),
+      .tv_usec = static_cast<long>(microseconds.count()),
+    };
 
-struct debugger_thread_info {
-  pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-  pid_t crashing_tid;
-  pid_t pseudothread_tid;
-  int signal_number;
-  siginfo_t* info;
-};
+    if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) {
+      return false;
+    }
+    if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != 0) {
+      return false;
+    }
 
-// Logging and contacting debuggerd requires free file descriptors, which we might not have.
-// Work around this by spawning a "thread" that shares its parent's address space, but not its file
-// descriptor table, so that we can close random file descriptors without affecting the original
-// process. Note that this doesn't go through pthread_create, so TLS is shared with the spawning
-// process.
-static void* pseudothread_stack;
-static int debuggerd_dispatch_pseudothread(void* arg) {
-  debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);
-
-  for (int i = 3; i < 1024; ++i) {
-    close(i);
-  }
-
-  log_signal_summary(thread_info->signal_number, thread_info->info);
-  send_debuggerd_packet(thread_info->crashing_tid, thread_info->pseudothread_tid);
-  pthread_mutex_unlock(&thread_info->mutex);
-  return 0;
-}
-
-/*
- * Catches fatal signals so we can ask debuggerd to ptrace us before
- * we crash.
- */
-static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) {
-  // It's possible somebody cleared the SA_SIGINFO flag, which would mean
-  // our "info" arg holds an undefined value.
-  if (!have_siginfo(signal_number)) {
-    info = nullptr;
-  }
-
-  debugger_thread_info thread_info = {
-    .crashing_tid = gettid(),
-    .signal_number = signal_number,
-    .info = info
+    return true;
   };
 
-  pthread_mutex_lock(&thread_info.mutex);
-  pid_t child_pid = clone(debuggerd_dispatch_pseudothread, pseudothread_stack,
-                          CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID,
-                          &thread_info, nullptr, nullptr, &thread_info.pseudothread_tid);
-
-  if (child_pid == -1) {
-    fatal("failed to spawn debuggerd dispatch thread: %s", strerror(errno));
+  sockfd.reset(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
+  if (sockfd == -1) {
+    PLOG(ERROR) << "libdebugger_client: failed to create socket";
+    return false;
   }
 
-  // Wait for the child to finish and unlock the mutex.
-  // This relies on bionic behavior that isn't guaranteed by the standard.
-  pthread_mutex_lock(&thread_info.mutex);
-
-
-  // We need to return from the signal handler so that debuggerd can dump the
-  // thread that crashed, but returning here does not guarantee that the signal
-  // will be thrown again, even for SIGSEGV and friends, since the signal could
-  // have been sent manually. Resend the signal with rt_tgsigqueueinfo(2) to
-  // preserve the SA_SIGINFO contents.
-  signal(signal_number, SIG_DFL);
-
-  struct siginfo si;
-  if (!info) {
-    memset(&si, 0, sizeof(si));
-    si.si_code = SI_USER;
-    si.si_pid = getpid();
-    si.si_uid = getuid();
-    info = &si;
-  } else if (info->si_code >= 0 || info->si_code == SI_TKILL) {
-    // rt_tgsigqueueinfo(2)'s documentation appears to be incorrect on kernels
-    // that contain commit 66dd34a (3.9+). The manpage claims to only allow
-    // negative si_code values that are not SI_TKILL, but 66dd34a changed the
-    // check to allow all si_code values in calls coming from inside the house.
+  if (!set_timeout()) {
+    PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+    return false;
   }
 
-  int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), signal_number, info);
-  if (rc != 0) {
-    fatal("failed to resend signal during crash: %s", strerror(errno));
+  if (socket_local_client_connect(sockfd.get(), kTombstonedInterceptSocketName,
+                                  ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET) == -1) {
+    PLOG(ERROR) << "libdebuggerd_client: failed to connect to tombstoned";
+    return false;
   }
+
+  InterceptRequest req = {.pid = pid };
+  if (!set_timeout()) {
+    PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+  }
+
+  if (send_fd(sockfd.get(), &req, sizeof(req), std::move(output_fd)) != sizeof(req)) {
+    PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
+    return false;
+  }
+
+  bool backtrace = dump_type == kDebuggerdBacktrace;
+  send_signal(pid, backtrace);
+
+  if (!set_timeout()) {
+    PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+  }
+
+  InterceptResponse response;
+  ssize_t rc = TEMP_FAILURE_RETRY(recv(sockfd.get(), &response, sizeof(response), MSG_TRUNC));
+  if (rc == 0) {
+    LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
+    return false;
+  } else if (rc != sizeof(response)) {
+    LOG(ERROR)
+      << "libdebuggerd_client: received packet of unexpected length from tombstoned: expected "
+      << sizeof(response) << ", received " << rc;
+    return false;
+  }
+
+  if (response.success != 1) {
+    response.error_message[sizeof(response.error_message) - 1] = '\0';
+    LOG(ERROR) << "libdebuggerd_client: tombstoned reported failure: " << response.error_message;
+  }
+
+  LOG(INFO) << "libdebuggerd_client: done dumping process " << pid;
+
+  return true;
 }
 
-void debuggerd_init(debuggerd_callbacks_t* callbacks) {
-  if (callbacks) {
-    g_callbacks = *callbacks;
+int dump_backtrace_to_file(pid_t tid, int fd) {
+  return dump_backtrace_to_file_timeout(tid, fd, 0);
+}
+
+int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {
+  android::base::unique_fd copy(dup(fd));
+  if (copy == -1) {
+    return -1;
   }
-
-  void* thread_stack_allocation =
-    mmap(nullptr, PAGE_SIZE * 3, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
-  if (thread_stack_allocation == MAP_FAILED) {
-    fatal("failed to allocate debuggerd thread stack");
-  }
-
-  char* stack = static_cast<char*>(thread_stack_allocation) + PAGE_SIZE;
-  if (mprotect(stack, PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) {
-    fatal("failed to mprotect debuggerd thread stack");
-  }
-
-  // Stack grows negatively, set it to the last byte in the page...
-  stack = (stack + PAGE_SIZE - 1);
-  // and align it.
-  stack -= 15;
-  pseudothread_stack = stack;
-
-  struct sigaction action;
-  memset(&action, 0, sizeof(action));
-  sigemptyset(&action.sa_mask);
-  action.sa_sigaction = debuggerd_signal_handler;
-  action.sa_flags = SA_RESTART | SA_SIGINFO;
-
-  // Use the alternate signal stack if available so we can catch stack overflows.
-  action.sa_flags |= SA_ONSTACK;
-
-  sigaction(SIGABRT, &action, nullptr);
-  sigaction(SIGBUS, &action, nullptr);
-  sigaction(SIGFPE, &action, nullptr);
-  sigaction(SIGILL, &action, nullptr);
-  sigaction(SIGSEGV, &action, nullptr);
-#if defined(SIGSTKFLT)
-  sigaction(SIGSTKFLT, &action, nullptr);
-#endif
-  sigaction(SIGTRAP, &action, nullptr);
+  int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;
+  return debuggerd_trigger_dump(tid, std::move(copy), kDebuggerdBacktrace, timeout_ms) ? 0 : -1;
 }
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
new file mode 100644
index 0000000..0e15472
--- /dev/null
+++ b/debuggerd/crash_dump.cpp
@@ -0,0 +1,439 @@
+/*
+ * Copyright 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 <arpa/inet.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <syscall.h>
+#include <unistd.h>
+
+#include <limits>
+#include <memory>
+#include <set>
+#include <vector>
+
+#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/unique_fd.h>
+#include <cutils/sockets.h>
+#include <log/log.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/util.h"
+
+using android::base::unique_fd;
+using android::base::StringPrintf;
+
+static bool pid_contains_tid(int pid_proc_fd, pid_t tid) {
+  struct stat st;
+  std::string task_path = StringPrintf("task/%d", tid);
+  return fstatat(pid_proc_fd, task_path.c_str(), &st, 0) == 0;
+}
+
+// Attach to a thread, and verify that it's still a member of the given process
+static bool ptrace_seize_thread(int pid_proc_fd, pid_t tid, std::string* error) {
+  if (ptrace(PTRACE_SEIZE, tid, 0, 0) != 0) {
+    *error = StringPrintf("failed to attach to thread %d: %s", tid, strerror(errno));
+    return false;
+  }
+
+  // Make sure that the task we attached to is actually part of the pid we're dumping.
+  if (!pid_contains_tid(pid_proc_fd, tid)) {
+    if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
+      PLOG(FATAL) << "failed to detach from thread " << tid;
+    }
+    *error = StringPrintf("thread %d is not in process", tid);
+    return false;
+  }
+
+  // Put the task into ptrace-stop state.
+  if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) != 0) {
+    PLOG(FATAL) << "failed to interrupt thread " << tid;
+  }
+
+  return true;
+}
+
+static bool activity_manager_notify(int pid, int signal, const std::string& amfd_data) {
+  android::base::unique_fd amfd(socket_local_client("/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM));
+  if (amfd.get() == -1) {
+    PLOG(ERROR) << "unable to connect to activity manager";
+    return false;
+  }
+
+  struct timeval tv = {
+    .tv_sec = 1,
+    .tv_usec = 0,
+  };
+  if (setsockopt(amfd.get(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
+    PLOG(ERROR) << "failed to set send timeout on activity manager socket";
+    return false;
+  }
+  tv.tv_sec = 3;  // 3 seconds on handshake read
+  if (setsockopt(amfd.get(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
+    PLOG(ERROR) << "failed to set receive timeout on activity manager socket";
+    return false;
+  }
+
+  // Activity Manager protocol: binary 32-bit network-byte-order ints for the
+  // pid and signal number, followed by the raw text of the dump, culminating
+  // in a zero byte that marks end-of-data.
+  uint32_t datum = htonl(pid);
+  if (!android::base::WriteFully(amfd, &datum, 4)) {
+    PLOG(ERROR) << "AM pid write failed";
+    return false;
+  }
+  datum = htonl(signal);
+  if (!android::base::WriteFully(amfd, &datum, 4)) {
+    PLOG(ERROR) << "AM signal write failed";
+    return false;
+  }
+  if (!android::base::WriteFully(amfd, amfd_data.c_str(), amfd_data.size() + 1)) {
+    PLOG(ERROR) << "AM data write failed";
+    return false;
+  }
+
+  // 3 sec timeout reading the ack; we're fine if the read fails.
+  char ack;
+  android::base::ReadFully(amfd, &ack, 1);
+  return true;
+}
+
+static 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));
+  if (sockfd == -1) {
+    PLOG(ERROR) << "failed to connect to tombstoned";
+    return false;
+  }
+
+  TombstonedCrashPacket packet = {};
+  packet.packet_type = CrashPacketType::kDumpRequest;
+  packet.packet.dump_request.pid = pid;
+  if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
+    PLOG(ERROR) << "failed to write DumpRequest packet";
+    return false;
+  }
+
+  unique_fd tmp_output_fd;
+  ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd);
+  if (rc == -1) {
+    PLOG(ERROR) << "failed to read response to DumpRequest packet";
+    return false;
+  } else if (rc != sizeof(packet)) {
+    LOG(ERROR) << "read DumpRequest response packet of incorrect length (expected "
+               << sizeof(packet) << ", got " << rc << ")";
+    return false;
+  }
+
+  // Make the fd O_APPEND so that our output is guaranteed to be at the end of a file.
+  // (This also makes selinux rules consistent, because selinux distinguishes between writing to
+  // 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) {
+    PLOG(WARNING) << "failed to set output fd flags";
+  }
+
+  *tombstoned_socket = std::move(sockfd);
+  *output_fd = std::move(tmp_output_fd);
+  return true;
+}
+
+static bool tombstoned_notify_completion(int tombstoned_socket) {
+  TombstonedCrashPacket packet = {};
+  packet.packet_type = CrashPacketType::kCompletedDump;
+  if (TEMP_FAILURE_RETRY(write(tombstoned_socket, &packet, sizeof(packet))) != sizeof(packet)) {
+    return false;
+  }
+  return true;
+}
+
+static void signal_handler(int) {
+  // We can't log easily, because the heap might be corrupt.
+  // Just die and let the surrounding log context explain things.
+  _exit(1);
+}
+
+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)) {
+      // We failed to connect, not much we can do.
+      LOG(ERROR) << "failed to connected to tombstoned to report failure";
+      _exit(1);
+    }
+  }
+
+  dprintf(output_fd.get(), "crash_dump failed to dump process %d: %s\n", target, abort_msg);
+
+  _exit(1);
+}
+
+static void drop_capabilities() {
+  __user_cap_header_struct capheader;
+  memset(&capheader, 0, sizeof(capheader));
+  capheader.version = _LINUX_CAPABILITY_VERSION_3;
+  capheader.pid = 0;
+
+  __user_cap_data_struct capdata[2];
+  memset(&capdata, 0, sizeof(capdata));
+
+  if (capset(&capheader, &capdata[0]) == -1) {
+    PLOG(FATAL) << "failed to drop capabilities";
+  }
+
+  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
+    PLOG(FATAL) << "failed to set PR_SET_NO_NEW_PRIVS";
+  }
+}
+
+int main(int argc, char** argv) {
+  pid_t target = getppid();
+  bool tombstoned_connected = false;
+  unique_fd tombstoned_socket;
+  unique_fd output_fd;
+
+  android::base::InitLogging(argv);
+  android::base::SetAborter([&](const char* abort_msg) {
+    abort_handler(target, tombstoned_connected, tombstoned_socket, output_fd, abort_msg);
+  });
+
+  // Don't try to dump ourselves.
+  struct sigaction action = {};
+  action.sa_handler = signal_handler;
+  debuggerd_register_handlers(&action);
+
+  if (argc != 3) {
+    return 1;
+  }
+
+  pid_t main_tid;
+  pid_t pseudothread_tid;
+
+  if (target == 1) {
+    LOG(FATAL) << "target died before we could attach";
+  }
+
+  if (!android::base::ParseInt(argv[1], &main_tid, 1, std::numeric_limits<pid_t>::max())) {
+    LOG(FATAL) << "invalid main tid: " << argv[1];
+  }
+
+  if (!android::base::ParseInt(argv[2], &pseudothread_tid, 1, std::numeric_limits<pid_t>::max())) {
+    LOG(FATAL) << "invalid pseudothread tid: " << argv[2];
+  }
+
+  android::procinfo::ProcessInfo target_info;
+  if (!android::procinfo::GetProcessInfo(main_tid, &target_info)) {
+    LOG(FATAL) << "failed to fetch process info for target " << main_tid;
+  }
+
+  if (main_tid != target_info.tid || target != target_info.pid) {
+    LOG(FATAL) << "target info mismatch, expected pid " << target << ", tid " << main_tid
+               << ", received pid " << target_info.pid << ", tid " << target_info.tid;
+  }
+
+  // Open /proc/`getppid()` in the original process, and pass it down to the forked child.
+  std::string target_proc_path = "/proc/" + std::to_string(target);
+  int target_proc_fd = open(target_proc_path.c_str(), O_DIRECTORY | O_RDONLY);
+  if (target_proc_fd == -1) {
+    PLOG(FATAL) << "failed to open " << target_proc_path;
+  }
+
+  // Make sure our parent didn't die.
+  if (getppid() != target) {
+    PLOG(FATAL) << "parent died";
+  }
+
+  // Reparent ourselves to init, so that the signal handler can waitpid on the
+  // original process to avoid leaving a zombie for non-fatal dumps.
+  pid_t forkpid = fork();
+  if (forkpid == -1) {
+    PLOG(FATAL) << "fork failed";
+  } else if (forkpid != 0) {
+    exit(0);
+  }
+
+  // Die if we take too long.
+  alarm(20);
+
+  std::string attach_error;
+
+  // Seize the main thread.
+  if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
+    LOG(FATAL) << attach_error;
+  }
+
+  // Seize the siblings.
+  std::set<pid_t> attached_siblings;
+  {
+    std::set<pid_t> siblings;
+    if (!android::procinfo::GetProcessTids(target, &siblings)) {
+      PLOG(FATAL) << "failed to get process siblings";
+    }
+
+    // but not the already attached main thread.
+    siblings.erase(main_tid);
+    // or the handler pseudothread.
+    siblings.erase(pseudothread_tid);
+
+    for (pid_t sibling_tid : siblings) {
+      if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) {
+        LOG(WARNING) << attach_error;
+      } else {
+        attached_siblings.insert(sibling_tid);
+      }
+    }
+  }
+
+  // Collect the backtrace map and open files, while the process still has PR_GET_DUMPABLE=1
+  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(main_tid));
+  if (!backtrace_map) {
+    LOG(FATAL) << "failed to create backtrace map";
+  }
+
+  // Collect the list of open files.
+  OpenFilesList open_files;
+  populate_open_files_list(target, &open_files);
+
+  // 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);
+
+  // Write a '\1' to stdout to tell the crashing process to resume.
+  // It also restores the value of PR_SET_DUMPABLE at this point.
+  if (TEMP_FAILURE_RETRY(write(STDOUT_FILENO, "\1", 1)) == -1) {
+    PLOG(ERROR) << "failed to communicate to target process";
+  }
+
+  if (tombstoned_connected) {
+    if (TEMP_FAILURE_RETRY(dup2(output_fd.get(), STDOUT_FILENO)) == -1) {
+      PLOG(ERROR) << "failed to dup2 output fd (" << output_fd.get() << ") to STDOUT_FILENO";
+    }
+  } else {
+    unique_fd devnull(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
+    TEMP_FAILURE_RETRY(dup2(devnull.get(), STDOUT_FILENO));
+    output_fd = std::move(devnull);
+  }
+
+  LOG(INFO) << "performing dump of process " << target << " (target tid = " << main_tid << ")";
+
+  // At this point, the thread that made the request has been attached and is
+  // in ptrace-stopped state. After resumption, the triggering signal that has
+  // been queued will be delivered.
+  if (ptrace(PTRACE_CONT, main_tid, 0, 0) != 0) {
+    PLOG(ERROR) << "PTRACE_CONT(" << main_tid << ") failed";
+    exit(1);
+  }
+
+  siginfo_t siginfo = {};
+  if (!wait_for_signal(main_tid, &siginfo)) {
+    printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
+    exit(1);
+  }
+
+  int signo = siginfo.si_signo;
+  bool fatal_signal = signo != DEBUGGER_SIGNAL;
+  bool backtrace = false;
+  uintptr_t abort_address = 0;
+
+  // si_value can represent three things:
+  //   0: dump tombstone
+  //   1: dump backtrace
+  //   everything else: abort message address (implies dump tombstone)
+  if (siginfo.si_value.sival_int == 1) {
+    backtrace = true;
+  } else if (siginfo.si_value.sival_ptr != nullptr) {
+    abort_address = reinterpret_cast<uintptr_t>(siginfo.si_value.sival_ptr);
+  }
+
+  // TODO: Use seccomp to lock ourselves down.
+
+  std::string amfd_data;
+  if (backtrace) {
+    dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, attached_siblings, 0);
+  } else {
+    engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid,
+                      &attached_siblings, abort_address, fatal_signal ? &amfd_data : nullptr);
+  }
+
+  // We don't actually need to PTRACE_DETACH, as long as our tracees aren't in
+  // group-stop state, which is true as long as no stopping signals are sent.
+
+  bool wait_for_gdb = android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false);
+  if (!fatal_signal || siginfo.si_code == SI_USER) {
+    // Don't wait_for_gdb when the process didn't actually crash.
+    wait_for_gdb = false;
+  }
+
+  // If the process crashed or we need to send it SIGSTOP for wait_for_gdb,
+  // get it in a state where it can receive signals, and then send the relevant
+  // signal.
+  if (wait_for_gdb || fatal_signal) {
+    if (ptrace(PTRACE_INTERRUPT, main_tid, 0, 0) != 0) {
+      PLOG(ERROR) << "failed to use PTRACE_INTERRUPT on " << main_tid;
+    }
+
+    if (tgkill(target, main_tid, wait_for_gdb ? SIGSTOP : signo) != 0) {
+      PLOG(ERROR) << "failed to resend signal " << signo << " to " << main_tid;
+    }
+  }
+
+  if (wait_for_gdb) {
+    // Use ALOGI to line up with output from engrave_tombstone.
+    ALOGI(
+      "***********************************************************\n"
+      "* Process %d has been suspended while crashing.\n"
+      "* To attach gdbserver and start gdb, run this on the host:\n"
+      "*\n"
+      "*     gdbclient.py -p %d\n"
+      "*\n"
+      "***********************************************************",
+      target, main_tid);
+  }
+
+  if (fatal_signal) {
+    activity_manager_notify(target, signo, amfd_data);
+  }
+
+  // Close stdout before we notify tombstoned of completion.
+  close(STDOUT_FILENO);
+  if (tombstoned_connected && !tombstoned_notify_completion(tombstoned_socket.get())) {
+    LOG(ERROR) << "failed to notify tombstoned of completion";
+  }
+
+  return 0;
+}
diff --git a/debuggerd/crasher.cpp b/debuggerd/crasher.cpp
deleted file mode 100644
index b0e8b17..0000000
--- a/debuggerd/crasher.cpp
+++ /dev/null
@@ -1,275 +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.
- */
-
-#define LOG_TAG "crasher"
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <sched.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/cdefs.h>
-#include <sys/mman.h>
-#include <sys/ptrace.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <android/log.h>
-#include <cutils/sockets.h>
-
-#if defined(STATIC_CRASHER)
-#include "debuggerd/client.h"
-#endif
-
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
-extern const char* __progname;
-
-extern "C" void crash1(void);
-extern "C" void crashnostack(void);
-
-static int do_action(const char* arg);
-
-static void maybe_abort() {
-    if (time(0) != 42) {
-        abort();
-    }
-}
-
-static char* smash_stack_dummy_buf;
-__attribute__ ((noinline)) static void smash_stack_dummy_function(volatile int* plen) {
-  smash_stack_dummy_buf[*plen] = 0;
-}
-
-// This must be marked with "__attribute__ ((noinline))", to ensure the
-// compiler generates the proper stack guards around this function.
-// Assign local array address to global variable to force stack guards.
-// Use another noinline function to corrupt the stack.
-__attribute__ ((noinline)) static int smash_stack(volatile int* plen) {
-    printf("%s: deliberately corrupting stack...\n", __progname);
-
-    char buf[128];
-    smash_stack_dummy_buf = buf;
-    // This should corrupt stack guards and make process abort.
-    smash_stack_dummy_function(plen);
-    return 0;
-}
-
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Winfinite-recursion"
-#endif
-
-static void* global = 0; // So GCC doesn't optimize the tail recursion out of overflow_stack.
-
-__attribute__((noinline)) static void overflow_stack(void* p) {
-    void* buf[1];
-    buf[0] = p;
-    global = buf;
-    overflow_stack(&buf);
-}
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
-static void *noisy(void *x)
-{
-    char c = (uintptr_t) x;
-    for(;;) {
-        usleep(250*1000);
-        write(2, &c, 1);
-        if(c == 'C') *((volatile unsigned*) 0) = 42;
-    }
-    return NULL;
-}
-
-static int ctest()
-{
-    pthread_t thr;
-    pthread_attr_t attr;
-    pthread_attr_init(&attr);
-    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-    pthread_create(&thr, &attr, noisy, (void*) 'A');
-    pthread_create(&thr, &attr, noisy, (void*) 'B');
-    pthread_create(&thr, &attr, noisy, (void*) 'C');
-    for(;;) ;
-    return 0;
-}
-
-static void* thread_callback(void* raw_arg)
-{
-    return (void*) (uintptr_t) do_action((const char*) raw_arg);
-}
-
-static int do_action_on_thread(const char* arg)
-{
-    pthread_t t;
-    pthread_create(&t, NULL, thread_callback, (void*) arg);
-    void* result = NULL;
-    pthread_join(t, &result);
-    return (int) (uintptr_t) result;
-}
-
-__attribute__((noinline)) static int crash3(int a) {
-    *((int*) 0xdead) = a;
-    return a*4;
-}
-
-__attribute__((noinline)) static int crash2(int a) {
-    a = crash3(a) + 2;
-    return a*3;
-}
-
-__attribute__((noinline)) static int crash(int a) {
-    a = crash2(a) + 1;
-    return a*2;
-}
-
-static void abuse_heap() {
-    char buf[16];
-    free((void*) buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
-}
-
-static void sigsegv_non_null() {
-    int* a = (int *)(&do_action);
-    *a = 42;
-}
-
-static int do_action(const char* arg)
-{
-    fprintf(stderr, "%s: init pid=%d tid=%d\n", __progname, getpid(), gettid());
-
-    if (!strncmp(arg, "wait-", strlen("wait-"))) {
-      char buf[1];
-      TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf)));
-      return do_action(arg + strlen("wait-"));
-    } else if (!strncmp(arg, "exhaustfd-", strlen("exhaustfd-"))) {
-      errno = 0;
-      while (errno != EMFILE) {
-        open("/dev/null", O_RDONLY);
-      }
-      return do_action(arg + strlen("exhaustfd-"));
-    } else if (!strncmp(arg, "thread-", strlen("thread-"))) {
-        return do_action_on_thread(arg + strlen("thread-"));
-    } else if (!strcmp(arg, "SIGSEGV-non-null")) {
-        sigsegv_non_null();
-    } else if (!strcmp(arg, "smash-stack")) {
-        volatile int len = 128;
-        return smash_stack(&len);
-    } else if (!strcmp(arg, "stack-overflow")) {
-        overflow_stack(NULL);
-    } else if (!strcmp(arg, "nostack")) {
-        crashnostack();
-    } else if (!strcmp(arg, "ctest")) {
-        return ctest();
-    } else if (!strcmp(arg, "exit")) {
-        exit(1);
-    } else if (!strcmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) {
-        return crash(42);
-    } else if (!strcmp(arg, "abort")) {
-        maybe_abort();
-    } else if (!strcmp(arg, "assert")) {
-        __assert("some_file.c", 123, "false");
-    } else if (!strcmp(arg, "assert2")) {
-        __assert2("some_file.c", 123, "some_function", "false");
-    } else if (!strcmp(arg, "fortify")) {
-        char buf[10];
-        __read_chk(-1, buf, 32, 10);
-        while (true) pause();
-    } else if (!strcmp(arg, "LOG_ALWAYS_FATAL")) {
-        LOG_ALWAYS_FATAL("hello %s", "world");
-    } else if (!strcmp(arg, "LOG_ALWAYS_FATAL_IF")) {
-        LOG_ALWAYS_FATAL_IF(true, "hello %s", "world");
-    } else if (!strcmp(arg, "SIGFPE")) {
-        raise(SIGFPE);
-        return EXIT_SUCCESS;
-    } else if (!strcmp(arg, "SIGTRAP")) {
-        raise(SIGTRAP);
-        return EXIT_SUCCESS;
-    } else if (!strcmp(arg, "heap-usage")) {
-        abuse_heap();
-    } else if (!strcmp(arg, "SIGSEGV-unmapped")) {
-        char* map = reinterpret_cast<char*>(mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0));
-        munmap(map, sizeof(int));
-        map[0] = '8';
-    }
-
-    fprintf(stderr, "%s OP\n", __progname);
-    fprintf(stderr, "where OP is:\n");
-    fprintf(stderr, "  smash-stack           overwrite a stack-guard canary\n");
-    fprintf(stderr, "  stack-overflow        recurse until the stack overflows\n");
-    fprintf(stderr, "  heap-corruption       cause a libc abort by corrupting the heap\n");
-    fprintf(stderr, "  heap-usage            cause a libc abort by abusing a heap function\n");
-    fprintf(stderr, "  nostack               crash with a NULL stack pointer\n");
-    fprintf(stderr, "  ctest                 (obsoleted by thread-crash?)\n");
-    fprintf(stderr, "  exit                  call exit(1)\n");
-    fprintf(stderr, "  abort                 call abort()\n");
-    fprintf(stderr, "  assert                call assert() without a function\n");
-    fprintf(stderr, "  assert2               call assert() with a function\n");
-    fprintf(stderr, "  fortify               fail a _FORTIFY_SOURCE check\n");
-    fprintf(stderr, "  LOG_ALWAYS_FATAL      call LOG_ALWAYS_FATAL\n");
-    fprintf(stderr, "  LOG_ALWAYS_FATAL_IF   call LOG_ALWAYS_FATAL\n");
-    fprintf(stderr, "  SIGFPE                cause a SIGFPE\n");
-    fprintf(stderr, "  SIGSEGV               cause a SIGSEGV at address 0x0 (synonym: crash)\n");
-    fprintf(stderr, "  SIGSEGV-non-null      cause a SIGSEGV at a non-zero address\n");
-    fprintf(stderr, "  SIGSEGV-unmapped      mmap/munmap a region of memory and then attempt to access it\n");
-    fprintf(stderr, "  SIGTRAP               cause a SIGTRAP\n");
-    fprintf(stderr, "prefix any of the above with 'thread-' to not run\n");
-    fprintf(stderr, "on the process' main thread.\n");
-    fprintf(stderr, "prefix any of the above with 'exhaustfd-' to exhaust\n");
-    fprintf(stderr, "all available file descriptors before crashing.\n");
-    fprintf(stderr, "prefix any of the above with 'wait-' to wait until input is received on stdin\n");
-
-    return EXIT_SUCCESS;
-}
-
-int main(int argc, char **argv)
-{
-    fprintf(stderr, "%s: built at " __TIME__ "!@\n", __progname);
-
-#if defined(STATIC_CRASHER)
-    debuggerd_callbacks_t callbacks = {
-      .get_abort_message = []() {
-        static struct {
-          size_t size;
-          char msg[32];
-        } msg;
-
-        msg.size = strlen("dummy abort message");
-        memcpy(msg.msg, "dummy abort message", strlen("dummy abort message"));
-        return reinterpret_cast<abort_msg_t*>(&msg);
-      },
-      .post_dump = nullptr
-    };
-    debuggerd_init(&callbacks);
-#endif
-
-    if (argc > 1) {
-        return do_action(argv[1]);
-    } else {
-        crash1();
-    }
-
-    return 0;
-}
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
new file mode 100644
index 0000000..f73f672
--- /dev/null
+++ b/debuggerd/crasher/Android.bp
@@ -0,0 +1,83 @@
+cc_defaults {
+    name: "crasher-defaults",
+
+    cppflags: [
+        "-std=gnu++14",
+        "-W",
+        "-Wall",
+        "-Wextra",
+        "-Wunused",
+        "-Werror",
+        "-O0",
+        "-fstack-protector-all",
+        "-Wno-free-nonheap-object",
+        "-Wno-date-time",
+    ],
+    srcs: ["crasher.cpp"],
+    arch: {
+        arm: {
+            srcs: ["arm/crashglue.S"],
+
+            armv7_a_neon: {
+                asflags: ["-DHAS_VFP_D32"],
+            },
+        },
+        arm64: {
+            srcs: ["arm64/crashglue.S"],
+        },
+        mips: {
+            srcs: ["mips/crashglue.S"],
+        },
+        mips64: {
+            srcs: ["mips64/crashglue.S"],
+        },
+        x86: {
+            srcs: ["x86/crashglue.S"],
+        },
+        x86_64: {
+            srcs: ["x86_64/crashglue.S"],
+        },
+    },
+    compile_multilib: "both",
+}
+
+cc_binary {
+    name: "crasher",
+
+    defaults: ["crasher-defaults"],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libseccomp_policy",
+    ],
+    multilib: {
+        lib32: {
+            stem: "crasher",
+        },
+        lib64: {
+            stem: "crasher64",
+        },
+    },
+}
+
+cc_binary {
+    name: "static_crasher",
+
+    defaults: ["crasher-defaults"],
+    cppflags: ["-DSTATIC_CRASHER"],
+    static_executable: true,
+    static_libs: [
+        "libdebuggerd_handler",
+        "libbase",
+        "liblog",
+        "libseccomp_policy",
+    ],
+    multilib: {
+        lib32: {
+            stem: "static_crasher",
+        },
+        lib64: {
+            stem: "static_crasher64",
+        },
+    },
+}
diff --git a/debuggerd/arm/crashglue.S b/debuggerd/crasher/arm/crashglue.S
similarity index 100%
rename from debuggerd/arm/crashglue.S
rename to debuggerd/crasher/arm/crashglue.S
diff --git a/debuggerd/arm64/crashglue.S b/debuggerd/crasher/arm64/crashglue.S
similarity index 100%
rename from debuggerd/arm64/crashglue.S
rename to debuggerd/crasher/arm64/crashglue.S
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
new file mode 100644
index 0000000..6970201
--- /dev/null
+++ b/debuggerd/crasher/crasher.cpp
@@ -0,0 +1,326 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "crasher"
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+// We test both kinds of logging.
+#include <android-base/logging.h>
+#include <log/log.h>
+
+#include "seccomp_policy.h"
+
+#if defined(STATIC_CRASHER)
+#include "debuggerd/handler.h"
+#endif
+
+#if defined(__arm__)
+// See https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt for details.
+#define __kuser_helper_version (*(int32_t*) 0xffff0ffc)
+typedef void * (__kuser_get_tls_t)(void);
+#define __kuser_get_tls (*(__kuser_get_tls_t*) 0xffff0fe0)
+typedef int (__kuser_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
+#define __kuser_cmpxchg (*(__kuser_cmpxchg_t*) 0xffff0fc0)
+typedef void (__kuser_dmb_t)(void);
+#define __kuser_dmb (*(__kuser_dmb_t*) 0xffff0fa0)
+typedef int (__kuser_cmpxchg64_t)(const int64_t*, const int64_t*, volatile int64_t*);
+#define __kuser_cmpxchg64 (*(__kuser_cmpxchg64_t*) 0xffff0f60)
+#endif
+
+#define noinline __attribute__((__noinline__))
+
+// Avoid name mangling so that stacks are more readable.
+extern "C" {
+
+void crash1(void);
+void crashnostack(void);
+
+int do_action(const char* arg);
+
+noinline void maybe_abort() {
+    if (time(0) != 42) {
+        abort();
+    }
+}
+
+char* smash_stack_dummy_buf;
+noinline void smash_stack_dummy_function(volatile int* plen) {
+  smash_stack_dummy_buf[*plen] = 0;
+}
+
+// This must be marked with "__attribute__ ((noinline))", to ensure the
+// compiler generates the proper stack guards around this function.
+// Assign local array address to global variable to force stack guards.
+// Use another noinline function to corrupt the stack.
+noinline int smash_stack(volatile int* plen) {
+    printf("%s: deliberately corrupting stack...\n", getprogname());
+
+    char buf[128];
+    smash_stack_dummy_buf = buf;
+    // This should corrupt stack guards and make process abort.
+    smash_stack_dummy_function(plen);
+    return 0;
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winfinite-recursion"
+
+void* global = 0; // So GCC doesn't optimize the tail recursion out of overflow_stack.
+
+noinline void overflow_stack(void* p) {
+    void* buf[1];
+    buf[0] = p;
+    global = buf;
+    overflow_stack(&buf);
+}
+
+#pragma clang diagnostic pop
+
+noinline void* thread_callback(void* raw_arg) {
+    const char* arg = reinterpret_cast<const char*>(raw_arg);
+    return reinterpret_cast<void*>(static_cast<uintptr_t>(do_action(arg)));
+}
+
+noinline int do_action_on_thread(const char* arg) {
+    pthread_t t;
+    pthread_create(&t, nullptr, thread_callback, const_cast<char*>(arg));
+    void* result = nullptr;
+    pthread_join(t, &result);
+    return reinterpret_cast<uintptr_t>(result);
+}
+
+noinline int crash3(int a) {
+    *reinterpret_cast<int*>(0xdead) = a;
+    return a*4;
+}
+
+noinline int crash2(int a) {
+    a = crash3(a) + 2;
+    return a*3;
+}
+
+noinline int crash(int a) {
+    a = crash2(a) + 1;
+    return a*2;
+}
+
+noinline void abuse_heap() {
+    char buf[16];
+    free(buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
+}
+
+noinline void sigsegv_non_null() {
+    int* a = (int *)(&do_action);
+    *a = 42;
+}
+
+noinline void fprintf_null() {
+    fprintf(nullptr, "oops");
+}
+
+noinline void readdir_null() {
+    readdir(nullptr);
+}
+
+noinline int strlen_null() {
+    char* sneaky_null = nullptr;
+    return strlen(sneaky_null);
+}
+
+static int usage() {
+    fprintf(stderr, "usage: %s KIND\n", getprogname());
+    fprintf(stderr, "\n");
+    fprintf(stderr, "where KIND is:\n");
+    fprintf(stderr, "  smash-stack           overwrite a -fstack-protector guard\n");
+    fprintf(stderr, "  stack-overflow        recurse until the stack overflows\n");
+    fprintf(stderr, "  nostack               crash with a NULL stack pointer\n");
+    fprintf(stderr, "\n");
+    fprintf(stderr, "  heap-corruption       cause a libc abort by corrupting the heap\n");
+    fprintf(stderr, "  heap-usage            cause a libc abort by abusing a heap function\n");
+    fprintf(stderr, "\n");
+    fprintf(stderr, "  abort                 call abort()\n");
+    fprintf(stderr, "  assert                call assert() without a function\n");
+    fprintf(stderr, "  assert2               call assert() with a function\n");
+    fprintf(stderr, "  exit                  call exit(1)\n");
+    fprintf(stderr, "\n");
+    fprintf(stderr, "  fortify               fail a _FORTIFY_SOURCE check\n");
+    fprintf(stderr, "  seccomp               fail a seccomp check\n");
+#if defined(__arm__)
+    fprintf(stderr, "  kuser_helper_version  call kuser_helper_version\n");
+    fprintf(stderr, "  kuser_get_tls         call kuser_get_tls\n");
+    fprintf(stderr, "  kuser_cmpxchg         call kuser_cmpxchg\n");
+    fprintf(stderr, "  kuser_memory_barrier  call kuser_memory_barrier\n");
+    fprintf(stderr, "  kuser_cmpxchg64       call kuser_cmpxchg64\n");
+#endif
+    fprintf(stderr, "\n");
+    fprintf(stderr, "  LOG_ALWAYS_FATAL      call liblog LOG_ALWAYS_FATAL\n");
+    fprintf(stderr, "  LOG_ALWAYS_FATAL_IF   call liblog LOG_ALWAYS_FATAL_IF\n");
+    fprintf(stderr, "  LOG-FATAL             call libbase LOG(FATAL)\n");
+    fprintf(stderr, "\n");
+    fprintf(stderr, "  SIGFPE                cause a SIGFPE\n");
+    fprintf(stderr, "  SIGSEGV               cause a SIGSEGV at address 0x0 (synonym: crash)\n");
+    fprintf(stderr, "  SIGSEGV-non-null      cause a SIGSEGV at a non-zero address\n");
+    fprintf(stderr, "  SIGSEGV-unmapped      mmap/munmap a region of memory and then attempt to access it\n");
+    fprintf(stderr, "  SIGTRAP               cause a SIGTRAP\n");
+    fprintf(stderr, "\n");
+    fprintf(stderr, "  fprintf-NULL          pass a null pointer to fprintf\n");
+    fprintf(stderr, "  readdir-NULL          pass a null pointer to readdir\n");
+    fprintf(stderr, "  strlen-NULL           pass a null pointer to strlen\n");
+    fprintf(stderr, "  pthread_join-NULL     pass a null pointer to pthread_join\n");
+    fprintf(stderr, "\n");
+    fprintf(stderr, "  no_new_privs          set PR_SET_NO_NEW_PRIVS and then abort\n");
+    fprintf(stderr, "\n");
+    fprintf(stderr, "prefix any of the above with 'thread-' to run on a new thread\n");
+    fprintf(stderr, "prefix any of the above with 'exhaustfd-' to exhaust\n");
+    fprintf(stderr, "all available file descriptors before crashing.\n");
+    fprintf(stderr, "prefix any of the above with 'wait-' to wait until input is received on stdin\n");
+
+    return EXIT_FAILURE;
+}
+
+noinline int do_action(const char* arg) {
+    // Prefixes.
+    if (!strncmp(arg, "wait-", strlen("wait-"))) {
+      char buf[1];
+      TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf)));
+      return do_action(arg + strlen("wait-"));
+    } else if (!strncmp(arg, "exhaustfd-", strlen("exhaustfd-"))) {
+      errno = 0;
+      while (errno != EMFILE) {
+        open("/dev/null", O_RDONLY);
+      }
+      return do_action(arg + strlen("exhaustfd-"));
+    } else if (!strncmp(arg, "thread-", strlen("thread-"))) {
+        return do_action_on_thread(arg + strlen("thread-"));
+    }
+
+    // Actions.
+    if (!strcasecmp(arg, "SIGSEGV-non-null")) {
+        sigsegv_non_null();
+    } else if (!strcasecmp(arg, "smash-stack")) {
+        volatile int len = 128;
+        return smash_stack(&len);
+    } else if (!strcasecmp(arg, "stack-overflow")) {
+        overflow_stack(nullptr);
+    } else if (!strcasecmp(arg, "nostack")) {
+        crashnostack();
+    } else if (!strcasecmp(arg, "exit")) {
+        exit(1);
+    } else if (!strcasecmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) {
+        return crash(42);
+    } else if (!strcasecmp(arg, "abort")) {
+        maybe_abort();
+    } else if (!strcasecmp(arg, "assert")) {
+        __assert("some_file.c", 123, "false");
+    } else if (!strcasecmp(arg, "assert2")) {
+        __assert2("some_file.c", 123, "some_function", "false");
+    } else if (!strcasecmp(arg, "fortify")) {
+        char buf[10];
+        __read_chk(-1, buf, 32, 10);
+        while (true) pause();
+    } else if (!strcasecmp(arg, "LOG(FATAL)")) {
+        LOG(FATAL) << "hello " << 123;
+    } else if (!strcasecmp(arg, "LOG_ALWAYS_FATAL")) {
+        LOG_ALWAYS_FATAL("hello %s", "world");
+    } else if (!strcasecmp(arg, "LOG_ALWAYS_FATAL_IF")) {
+        LOG_ALWAYS_FATAL_IF(true, "hello %s", "world");
+    } else if (!strcasecmp(arg, "SIGFPE")) {
+        raise(SIGFPE);
+        return EXIT_SUCCESS;
+    } else if (!strcasecmp(arg, "SIGTRAP")) {
+        raise(SIGTRAP);
+        return EXIT_SUCCESS;
+    } else if (!strcasecmp(arg, "fprintf-NULL")) {
+        fprintf_null();
+    } else if (!strcasecmp(arg, "readdir-NULL")) {
+        readdir_null();
+    } else if (!strcasecmp(arg, "strlen-NULL")) {
+        return strlen_null();
+    } else if (!strcasecmp(arg, "pthread_join-NULL")) {
+        return pthread_join(0, nullptr);
+    } else if (!strcasecmp(arg, "heap-usage")) {
+        abuse_heap();
+    } else if (!strcasecmp(arg, "SIGSEGV-unmapped")) {
+        char* map = reinterpret_cast<char*>(mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE,
+                                                 MAP_SHARED | MAP_ANONYMOUS, -1, 0));
+        munmap(map, sizeof(int));
+        map[0] = '8';
+    } else if (!strcasecmp(arg, "seccomp")) {
+        set_seccomp_filter();
+        syscall(99999);
+#if defined(__arm__)
+    } else if (!strcasecmp(arg, "kuser_helper_version")) {
+        return __kuser_helper_version;
+    } else if (!strcasecmp(arg, "kuser_get_tls")) {
+        return !__kuser_get_tls();
+    } else if (!strcasecmp(arg, "kuser_cmpxchg")) {
+        return __kuser_cmpxchg(0, 0, 0);
+    } else if (!strcasecmp(arg, "kuser_memory_barrier")) {
+        __kuser_dmb();
+    } else if (!strcasecmp(arg, "kuser_cmpxchg64")) {
+        return __kuser_cmpxchg64(0, 0, 0);
+#endif
+    } else if (!strcasecmp(arg, "no_new_privs")) {
+        if (prctl(PR_SET_NO_NEW_PRIVS, 1) != 0) {
+          fprintf(stderr, "prctl(PR_SET_NO_NEW_PRIVS, 1) failed: %s\n", strerror(errno));
+          return EXIT_SUCCESS;
+        }
+        abort();
+    } else {
+        return usage();
+    }
+
+    fprintf(stderr, "%s: exiting normally!\n", getprogname());
+    return EXIT_SUCCESS;
+}
+
+int main(int argc, char** argv) {
+#if defined(STATIC_CRASHER)
+    debuggerd_callbacks_t callbacks = {
+      .get_abort_message = []() {
+        static struct {
+          size_t size;
+          char msg[32];
+        } msg;
+
+        msg.size = strlen("dummy abort message");
+        memcpy(msg.msg, "dummy abort message", strlen("dummy abort message"));
+        return reinterpret_cast<abort_msg_t*>(&msg);
+      },
+      .post_dump = nullptr
+    };
+    debuggerd_init(&callbacks);
+#endif
+
+    if (argc == 1) crash1();
+    else if (argc == 2) return do_action(argv[1]);
+
+    return usage();
+}
+
+};
diff --git a/debuggerd/mips/crashglue.S b/debuggerd/crasher/mips/crashglue.S
similarity index 100%
rename from debuggerd/mips/crashglue.S
rename to debuggerd/crasher/mips/crashglue.S
diff --git a/debuggerd/mips64/crashglue.S b/debuggerd/crasher/mips64/crashglue.S
similarity index 100%
rename from debuggerd/mips64/crashglue.S
rename to debuggerd/crasher/mips64/crashglue.S
diff --git a/debuggerd/x86/crashglue.S b/debuggerd/crasher/x86/crashglue.S
similarity index 100%
rename from debuggerd/x86/crashglue.S
rename to debuggerd/crasher/x86/crashglue.S
diff --git a/debuggerd/x86_64/crashglue.S b/debuggerd/crasher/x86_64/crashglue.S
similarity index 100%
rename from debuggerd/x86_64/crashglue.S
rename to debuggerd/crasher/x86_64/crashglue.S
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 9b82f64..492e9f0 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006, The Android Open Source Project
+ * Copyright 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.
@@ -14,937 +14,70 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "debuggerd"
-
-#include <arpa/inet.h>
-#include <dirent.h>
-#include <elf.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/input.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdarg.h>
+#include <err.h>
 #include <stdio.h>
-#include <sys/poll.h>
-#include <sys/prctl.h>
-#include <sys/ptrace.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/un.h>
-#include <time.h>
+#include <stdlib.h>
+#include <string.h>
 
-#include <memory>
-#include <set>
-#include <string>
+#include <limits>
+#include <thread>
 
-#include <selinux/android.h>
-
-#include <android/log.h>
 #include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
 #include <android-base/unique_fd.h>
-#include <cutils/debugger.h>
-#include <cutils/properties.h>
-#include <cutils/sockets.h>
-
-#include <private/android_filesystem_config.h>
-
 #include <debuggerd/client.h>
+#include <debuggerd/util.h>
+#include <selinux/selinux.h>
 
-#include "backtrace.h"
-#include "getevent.h"
-#include "open_files_list.h"
-#include "signal_sender.h"
-#include "tombstone.h"
-#include "utility.h"
+using android::base::unique_fd;
 
-// If the 32 bit executable is compiled on a 64 bit system,
-// use the 32 bit socket name.
-#if defined(TARGET_IS_64_BIT) && !defined(__LP64__)
-#define SOCKET_NAME DEBUGGER32_SOCKET_NAME
-#else
-#define SOCKET_NAME DEBUGGER_SOCKET_NAME
-#endif
+static void usage(int exit_code) {
+  fprintf(stderr, "usage: debuggerd [-b] PID\n");
+  fprintf(stderr, "\n");
+  fprintf(stderr, "-b, --backtrace    just a backtrace rather than a full tombstone\n");
+  _exit(exit_code);
+}
 
-struct debugger_request_t {
-  debugger_action_t action;
-  pid_t pid, tid;
-  uid_t uid, gid;
-  pid_t ignore_tid;
-  uintptr_t abort_msg_address;
-};
+static std::thread spawn_redirect_thread(unique_fd fd) {
+  return std::thread([fd{ std::move(fd) }]() {
+    while (true) {
+      char buf[BUFSIZ];
+      ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), buf, sizeof(buf)));
+      if (rc <= 0) {
+        return;
+      }
 
-static void wait_for_user_action(const debugger_request_t& request) {
-  // Explain how to attach the debugger.
-  ALOGI("***********************************************************\n"
-        "* Process %d has been suspended while crashing.\n"
-        "* To attach gdbserver and start gdb, run this on the host:\n"
-        "*\n"
-        "*     gdbclient.py -p %d\n"
-        "*\n"
-        "* Wait for gdb to start, then press the VOLUME DOWN key\n"
-        "* to let the process continue crashing.\n"
-        "***********************************************************",
-        request.pid, request.tid);
-
-  // Wait for VOLUME DOWN.
-  while (true) {
-    input_event e;
-    if (get_event(&e, -1) == 0) {
-      if (e.type == EV_KEY && e.code == KEY_VOLUMEDOWN && e.value == 0) {
-        break;
+      if (!android::base::WriteFully(STDOUT_FILENO, buf, rc)) {
+        return;
       }
     }
-  }
-
-  ALOGI("debuggerd resuming process %d", request.pid);
+  });
 }
 
-static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* out_gid) {
-  char path[64];
-  snprintf(path, sizeof(path), "/proc/%d/status", tid);
+int main(int argc, char* argv[]) {
+  if (argc <= 1) usage(0);
+  if (argc > 3) usage(1);
+  if (argc == 3 && strcmp(argv[1], "-b") != 0 && strcmp(argv[1], "--backtrace") != 0) usage(1);
+  bool backtrace_only = argc == 3;
 
-  FILE* fp = fopen(path, "r");
-  if (!fp) {
-    return -1;
+  pid_t pid;
+  if (!android::base::ParseInt(argv[argc - 1], &pid, 1, std::numeric_limits<pid_t>::max())) {
+    usage(1);
   }
 
-  int fields = 0;
-  char line[1024];
-  while (fgets(line, sizeof(line), fp)) {
-    size_t len = strlen(line);
-    if (len > 6 && !memcmp(line, "Tgid:\t", 6)) {
-      *out_pid = atoi(line + 6);
-      fields |= 1;
-    } else if (len > 5 && !memcmp(line, "Uid:\t", 5)) {
-      *out_uid = atoi(line + 5);
-      fields |= 2;
-    } else if (len > 5 && !memcmp(line, "Gid:\t", 5)) {
-      *out_gid = atoi(line + 5);
-      fields |= 4;
-    }
-  }
-  fclose(fp);
-  return fields == 7 ? 0 : -1;
-}
-
-/*
- * Corresponds with debugger_action_t enum type in
- * include/cutils/debugger.h.
- */
-static const char *debuggerd_perms[] = {
-  NULL, /* crash is only used on self, no check applied */
-  "dump_tombstone",
-  "dump_backtrace"
-};
-
-static int audit_callback(void* data, security_class_t /* cls */, char* buf, size_t len)
-{
-    struct debugger_request_t* req = reinterpret_cast<debugger_request_t*>(data);
-
-    if (!req) {
-        ALOGE("No debuggerd request audit data");
-        return 0;
-    }
-
-    snprintf(buf, len, "pid=%d uid=%d gid=%d", req->pid, req->uid, req->gid);
-    return 0;
-}
-
-static bool selinux_action_allowed(int s, debugger_request_t* request)
-{
-  char *scon = NULL, *tcon = NULL;
-  const char *tclass = "debuggerd";
-  const char *perm;
-  bool allowed = false;
-
-  if (request->action <= 0 || request->action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
-    ALOGE("SELinux:  No permission defined for debugger action %d", request->action);
-    return false;
+  unique_fd piperead, pipewrite;
+  if (!Pipe(&piperead, &pipewrite)) {
+    err(1, "failed to create pipe");
   }
 
-  perm = debuggerd_perms[request->action];
-
-  if (getpeercon(s, &scon) < 0) {
-    ALOGE("Cannot get peer context from socket\n");
-    goto out;
+  std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
+  if (!debuggerd_trigger_dump(pid, std::move(pipewrite),
+                              backtrace_only ? kDebuggerdBacktrace : kDebuggerdTombstone, 0)) {
+    redirect_thread.join();
+    errx(1, "failed to dump process %d", pid);
   }
 
-  if (getpidcon(request->tid, &tcon) < 0) {
-    ALOGE("Cannot get context for tid %d\n", request->tid);
-    goto out;
-  }
-
-  allowed = (selinux_check_access(scon, tcon, tclass, perm, reinterpret_cast<void*>(request)) == 0);
-
-out:
-   freecon(scon);
-   freecon(tcon);
-   return allowed;
-}
-
-static bool pid_contains_tid(pid_t pid, pid_t tid) {
-  char task_path[PATH_MAX];
-  if (snprintf(task_path, PATH_MAX, "/proc/%d/task/%d", pid, tid) >= PATH_MAX) {
-    ALOGE("debuggerd: task path overflow (pid = %d, tid = %d)\n", pid, tid);
-    exit(1);
-  }
-
-  return access(task_path, F_OK) == 0;
-}
-
-static int read_request(int fd, debugger_request_t* out_request) {
-  ucred cr;
-  socklen_t len = sizeof(cr);
-  int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
-  if (status != 0) {
-    ALOGE("cannot get credentials");
-    return -1;
-  }
-
-  ALOGV("reading tid");
-  pollfd pollfds[1];
-  pollfds[0].fd = fd;
-  pollfds[0].events = POLLIN;
-  pollfds[0].revents = 0;
-  status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000));
-  if (status != 1) {
-    ALOGE("timed out reading tid (from pid=%d uid=%d)\n", cr.pid, cr.uid);
-    return -1;
-  }
-
-  debugger_msg_t msg;
-  memset(&msg, 0, sizeof(msg));
-  status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg)));
-  if (status < 0) {
-    ALOGE("read failure? %s (pid=%d uid=%d)\n", strerror(errno), cr.pid, cr.uid);
-    return -1;
-  }
-  if (status != sizeof(debugger_msg_t)) {
-    ALOGE("invalid crash request of size %d (from pid=%d uid=%d)\n", status, cr.pid, cr.uid);
-    return -1;
-  }
-
-  out_request->action = static_cast<debugger_action_t>(msg.action);
-  out_request->tid = msg.tid;
-  out_request->ignore_tid = msg.ignore_tid;
-  out_request->pid = cr.pid;
-  out_request->uid = cr.uid;
-  out_request->gid = cr.gid;
-  out_request->abort_msg_address = msg.abort_msg_address;
-
-  if (msg.action == DEBUGGER_ACTION_CRASH) {
-    // Ensure that the tid reported by the crashing process is valid.
-    // This check needs to happen again after ptracing the requested thread to prevent a race.
-    if (!pid_contains_tid(out_request->pid, out_request->tid)) {
-      ALOGE("tid %d does not exist in pid %d. ignoring debug request\n", out_request->tid,
-            out_request->pid);
-      return -1;
-    }
-  } else if (cr.uid == 0 || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) {
-    // Only root or system can ask us to attach to any process and dump it explicitly.
-    // However, system is only allowed to collect backtraces but cannot dump tombstones.
-    status = get_process_info(out_request->tid, &out_request->pid,
-                              &out_request->uid, &out_request->gid);
-    if (status < 0) {
-      ALOGE("tid %d does not exist. ignoring explicit dump request\n", out_request->tid);
-      return -1;
-    }
-
-    if (!selinux_action_allowed(fd, out_request))
-      return -1;
-  } else {
-    // No one else is allowed to dump arbitrary processes.
-    return -1;
-  }
+  redirect_thread.join();
   return 0;
 }
-
-static int activity_manager_connect() {
-  android::base::unique_fd amfd(socket(PF_UNIX, SOCK_STREAM, 0));
-  if (amfd.get() < -1) {
-    ALOGE("debuggerd: Unable to connect to activity manager (socket failed: %s)", strerror(errno));
-    return -1;
-  }
-
-  struct sockaddr_un address;
-  memset(&address, 0, sizeof(address));
-  address.sun_family = AF_UNIX;
-  // The path used here must match the value defined in NativeCrashListener.java.
-  strncpy(address.sun_path, "/data/system/ndebugsocket", sizeof(address.sun_path));
-  if (TEMP_FAILURE_RETRY(connect(amfd.get(), reinterpret_cast<struct sockaddr*>(&address),
-                                 sizeof(address))) == -1) {
-    ALOGE("debuggerd: Unable to connect to activity manager (connect failed: %s)", strerror(errno));
-    return -1;
-  }
-
-  struct timeval tv;
-  memset(&tv, 0, sizeof(tv));
-  tv.tv_sec = 1;  // tight leash
-  if (setsockopt(amfd.get(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
-    ALOGE("debuggerd: Unable to connect to activity manager (setsockopt SO_SNDTIMEO failed: %s)",
-          strerror(errno));
-    return -1;
-  }
-
-  tv.tv_sec = 3;  // 3 seconds on handshake read
-  if (setsockopt(amfd.get(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
-    ALOGE("debuggerd: Unable to connect to activity manager (setsockopt SO_RCVTIMEO failed: %s)",
-          strerror(errno));
-    return -1;
-  }
-
-  return amfd.release();
-}
-
-static void activity_manager_write(int pid, int signal, int amfd, const std::string& amfd_data) {
-  if (amfd == -1) {
-    return;
-  }
-
-  // Activity Manager protocol: binary 32-bit network-byte-order ints for the
-  // pid and signal number, followed by the raw text of the dump, culminating
-  // in a zero byte that marks end-of-data.
-  uint32_t datum = htonl(pid);
-  if (!android::base::WriteFully(amfd, &datum, 4)) {
-    ALOGE("AM pid write failed: %s\n", strerror(errno));
-    return;
-  }
-  datum = htonl(signal);
-  if (!android::base::WriteFully(amfd, &datum, 4)) {
-    ALOGE("AM signal write failed: %s\n", strerror(errno));
-    return;
-  }
-
-  if (!android::base::WriteFully(amfd, amfd_data.c_str(), amfd_data.size())) {
-    ALOGE("AM data write failed: %s\n", strerror(errno));
-    return;
-  }
-
-  // Send EOD to the Activity Manager, then wait for its ack to avoid racing
-  // ahead and killing the target out from under it.
-  uint8_t eodMarker = 0;
-  if (!android::base::WriteFully(amfd, &eodMarker, 1)) {
-    ALOGE("AM eod write failed: %s\n", strerror(errno));
-    return;
-  }
-  // 3 sec timeout reading the ack; we're fine if the read fails.
-  android::base::ReadFully(amfd, &eodMarker, 1);
-}
-
-static bool should_attach_gdb(const debugger_request_t& request) {
-  if (request.action == DEBUGGER_ACTION_CRASH) {
-    return property_get_bool("debug.debuggerd.wait_for_gdb", false);
-  }
-  return false;
-}
-
-#if defined(__LP64__)
-static bool is32bit(pid_t tid) {
-  char* exeline;
-  if (asprintf(&exeline, "/proc/%d/exe", tid) == -1) {
-    return false;
-  }
-  int fd = TEMP_FAILURE_RETRY(open(exeline, O_RDONLY | O_CLOEXEC));
-  int saved_errno = errno;
-  free(exeline);
-  if (fd == -1) {
-    ALOGW("Failed to open /proc/%d/exe %s", tid, strerror(saved_errno));
-    return false;
-  }
-
-  char ehdr[EI_NIDENT];
-  ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &ehdr, sizeof(ehdr)));
-  close(fd);
-  if (bytes != (ssize_t) sizeof(ehdr) || memcmp(ELFMAG, ehdr, SELFMAG) != 0) {
-    return false;
-  }
-  if (ehdr[EI_CLASS] == ELFCLASS32) {
-    return true;
-  }
-  return false;
-}
-
-static void redirect_to_32(int fd, debugger_request_t* request) {
-  debugger_msg_t msg;
-  memset(&msg, 0, sizeof(msg));
-  msg.tid = request->tid;
-  msg.action = request->action;
-
-  int sock_fd = socket_local_client(DEBUGGER32_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
-                                    SOCK_STREAM | SOCK_CLOEXEC);
-  if (sock_fd < 0) {
-    ALOGE("Failed to connect to debuggerd32: %s", strerror(errno));
-    return;
-  }
-
-  if (TEMP_FAILURE_RETRY(write(sock_fd, &msg, sizeof(msg))) != (ssize_t) sizeof(msg)) {
-    ALOGE("Failed to write request to debuggerd32 socket: %s", strerror(errno));
-    close(sock_fd);
-    return;
-  }
-
-  char ack;
-  if (TEMP_FAILURE_RETRY(read(sock_fd, &ack, 1)) == -1) {
-    ALOGE("Failed to read ack from debuggerd32 socket: %s", strerror(errno));
-    close(sock_fd);
-    return;
-  }
-
-  char buffer[1024];
-  ssize_t bytes_read;
-  while ((bytes_read = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer)))) > 0) {
-    ssize_t bytes_to_send = bytes_read;
-    ssize_t bytes_written;
-    do {
-      bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer + bytes_read - bytes_to_send,
-                                               bytes_to_send));
-      if (bytes_written == -1) {
-        if (errno == EAGAIN) {
-          // Retry the write.
-          continue;
-        }
-        ALOGE("Error while writing data to fd: %s", strerror(errno));
-        break;
-      }
-      bytes_to_send -= bytes_written;
-    } while (bytes_written != 0 && bytes_to_send > 0);
-    if (bytes_to_send != 0) {
-        ALOGE("Failed to write all data to fd: read %zd, sent %zd", bytes_read, bytes_to_send);
-        break;
-    }
-  }
-  close(sock_fd);
-}
-#endif
-
-// Attach to a thread, and verify that it's still a member of the given process
-static bool ptrace_attach_thread(pid_t pid, pid_t tid) {
-  if (ptrace(PTRACE_ATTACH, tid, 0, 0) != 0) {
-    return false;
-  }
-
-  // Make sure that the task we attached to is actually part of the pid we're dumping.
-  if (!pid_contains_tid(pid, tid)) {
-    if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
-      ALOGE("debuggerd: failed to detach from thread '%d'", tid);
-      exit(1);
-    }
-    return false;
-  }
-
-  return true;
-}
-
-static void ptrace_siblings(pid_t pid, pid_t main_tid, pid_t ignore_tid, std::set<pid_t>& tids) {
-  char task_path[PATH_MAX];
-
-  if (snprintf(task_path, PATH_MAX, "/proc/%d/task", pid) >= PATH_MAX) {
-    ALOGE("debuggerd: task path overflow (pid = %d)\n", pid);
-    abort();
-  }
-
-  std::unique_ptr<DIR, int (*)(DIR*)> d(opendir(task_path), closedir);
-
-  // Bail early if the task directory cannot be opened.
-  if (!d) {
-    ALOGE("debuggerd: failed to open /proc/%d/task: %s", pid, strerror(errno));
-    return;
-  }
-
-  struct dirent* de;
-  while ((de = readdir(d.get())) != NULL) {
-    // Ignore "." and "..".
-    if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
-      continue;
-    }
-
-    char* end;
-    pid_t tid = strtoul(de->d_name, &end, 10);
-    if (*end) {
-      continue;
-    }
-
-    if (tid == main_tid || tid == ignore_tid) {
-      continue;
-    }
-
-    if (!ptrace_attach_thread(pid, tid)) {
-      ALOGE("debuggerd: ptrace attach to %d failed: %s", tid, strerror(errno));
-      continue;
-    }
-
-    tids.insert(tid);
-  }
-}
-
-static bool perform_dump(const debugger_request_t& request, int fd, int tombstone_fd,
-                         BacktraceMap* backtrace_map, const OpenFilesList& open_files,
-                         const std::set<pid_t>& siblings,
-                         int* crash_signal, std::string* amfd_data) {
-  if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
-    ALOGE("debuggerd: failed to respond to client: %s\n", strerror(errno));
-    return false;
-  }
-
-  while (true) {
-    // wait_for_signal waits for forever, but the watchdog process will kill us
-    // if it takes too long.
-    int signal = wait_for_signal(request.tid);
-    switch (signal) {
-      case -1:
-        ALOGE("debuggerd: timed out waiting for signal");
-        return false;
-
-      case SIGSTOP:
-        if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
-          ALOGV("debuggerd: stopped -- dumping to tombstone");
-          engrave_tombstone(tombstone_fd, backtrace_map, open_files,
-                            request.pid, request.tid, siblings,
-                            request.abort_msg_address, amfd_data);
-        } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
-          ALOGV("debuggerd: stopped -- dumping to fd");
-          dump_backtrace(fd, backtrace_map, request.pid, request.tid, siblings, nullptr);
-        } else {
-          ALOGV("debuggerd: stopped -- continuing");
-          if (ptrace(PTRACE_CONT, request.tid, 0, 0) != 0) {
-            ALOGE("debuggerd: ptrace continue failed: %s", strerror(errno));
-            return false;
-          }
-          continue;  // loop again
-        }
-        break;
-
-      case SIGABRT:
-      case SIGBUS:
-      case SIGFPE:
-      case SIGILL:
-      case SIGSEGV:
-#ifdef SIGSTKFLT
-      case SIGSTKFLT:
-#endif
-      case SIGSYS:
-      case SIGTRAP:
-        ALOGV("stopped -- fatal signal\n");
-        *crash_signal = signal;
-        engrave_tombstone(tombstone_fd, backtrace_map, open_files,
-                          request.pid, request.tid, siblings,
-                          request.abort_msg_address, amfd_data);
-        break;
-
-      default:
-        ALOGE("debuggerd: process stopped due to unexpected signal %d\n", signal);
-        break;
-    }
-    break;
-  }
-
-  return true;
-}
-
-static bool drop_privileges() {
-  // AID_LOG: for reading the logs data associated with the crashing process.
-  // AID_READPROC: for reading /proc/<PID>/{comm,cmdline}.
-  gid_t groups[] = { AID_DEBUGGERD, AID_LOG, AID_READPROC };
-  if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
-    ALOGE("debuggerd: failed to setgroups: %s", strerror(errno));
-    return false;
-  }
-
-  if (setresgid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
-    ALOGE("debuggerd: failed to setresgid: %s", strerror(errno));
-    return false;
-  }
-
-  if (setresuid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
-    ALOGE("debuggerd: failed to setresuid: %s", strerror(errno));
-    return false;
-  }
-
-  return true;
-}
-
-static void worker_process(int fd, debugger_request_t& request) {
-  // Open the tombstone file if we need it.
-  std::string tombstone_path;
-  int tombstone_fd = -1;
-  switch (request.action) {
-    case DEBUGGER_ACTION_DUMP_TOMBSTONE:
-    case DEBUGGER_ACTION_CRASH:
-      tombstone_fd = open_tombstone(&tombstone_path);
-      if (tombstone_fd == -1) {
-        ALOGE("debuggerd: failed to open tombstone file: %s\n", strerror(errno));
-        exit(1);
-      }
-      break;
-
-    case DEBUGGER_ACTION_DUMP_BACKTRACE:
-      break;
-
-    default:
-      ALOGE("debuggerd: unexpected request action: %d", request.action);
-      exit(1);
-  }
-
-  // At this point, the thread that made the request is blocked in
-  // a read() call.  If the thread has crashed, then this gives us
-  // time to PTRACE_ATTACH to it before it has a chance to really fault.
-  //
-  // The PTRACE_ATTACH sends a SIGSTOP to the target process, but it
-  // won't necessarily have stopped by the time ptrace() returns.  (We
-  // currently assume it does.)  We write to the file descriptor to
-  // ensure that it can run as soon as we call PTRACE_CONT below.
-  // See details in client/debuggerd_client.cpp, in function
-  // debugger_signal_handler().
-
-  // Attach to the target process.
-  if (!ptrace_attach_thread(request.pid, request.tid)) {
-    ALOGE("debuggerd: ptrace attach failed: %s", strerror(errno));
-    exit(1);
-  }
-
-  // DEBUGGER_ACTION_CRASH requests can come from arbitrary processes and the tid field in the
-  // request is sent from the other side. If an attacker can cause a process to be spawned with the
-  // pid of their process, they could trick debuggerd into dumping that process by exiting after
-  // sending the request. Validate the trusted request.uid/gid to defend against this.
-  if (request.action == DEBUGGER_ACTION_CRASH) {
-    pid_t pid;
-    uid_t uid;
-    gid_t gid;
-    if (get_process_info(request.tid, &pid, &uid, &gid) != 0) {
-      ALOGE("debuggerd: failed to get process info for tid '%d'", request.tid);
-      exit(1);
-    }
-
-    if (pid != request.pid || uid != request.uid || gid != request.gid) {
-      ALOGE(
-        "debuggerd: attached task %d does not match request: "
-        "expected pid=%d,uid=%d,gid=%d, actual pid=%d,uid=%d,gid=%d",
-        request.tid, request.pid, request.uid, request.gid, pid, uid, gid);
-      exit(1);
-    }
-  }
-
-  // Don't attach to the sibling threads if we want to attach gdb.
-  // Supposedly, it makes the process less reliable.
-  bool attach_gdb = should_attach_gdb(request);
-  if (attach_gdb) {
-    // Open all of the input devices we need to listen for VOLUMEDOWN before dropping privileges.
-    if (init_getevent() != 0) {
-      ALOGE("debuggerd: failed to initialize input device, not waiting for gdb");
-      attach_gdb = false;
-    }
-
-  }
-
-  std::set<pid_t> siblings;
-  if (!attach_gdb) {
-    ptrace_siblings(request.pid, request.tid, request.ignore_tid, siblings);
-  }
-
-  // Generate the backtrace map before dropping privileges.
-  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(request.pid));
-
-  // Collect the list of open files before dropping privileges.
-  OpenFilesList open_files;
-  populate_open_files_list(request.pid, &open_files);
-
-  int amfd = -1;
-  std::unique_ptr<std::string> amfd_data;
-  if (request.action == DEBUGGER_ACTION_CRASH) {
-    // Connect to the activity manager before dropping privileges.
-    amfd = activity_manager_connect();
-    amfd_data.reset(new std::string);
-  }
-
-  bool succeeded = false;
-
-  // Now that we've done everything that requires privileges, we can drop them.
-  if (!drop_privileges()) {
-    ALOGE("debuggerd: failed to drop privileges, exiting");
-    _exit(1);
-  }
-
-  int crash_signal = SIGKILL;
-  succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), open_files,
-                           siblings, &crash_signal, amfd_data.get());
-  if (succeeded) {
-    if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
-      if (!tombstone_path.empty()) {
-        android::base::WriteFully(fd, tombstone_path.c_str(), tombstone_path.length());
-      }
-    }
-  }
-
-  if (attach_gdb) {
-    // Tell the signal process to send SIGSTOP to the target.
-    if (!send_signal(request.pid, 0, SIGSTOP)) {
-      ALOGE("debuggerd: failed to stop process for gdb attach: %s", strerror(errno));
-      attach_gdb = false;
-    }
-  }
-
-  if (!attach_gdb) {
-    // Tell the Activity Manager about the crashing process. If we are
-    // waiting for gdb to attach, do not send this or Activity Manager
-    // might kill the process before anyone can attach.
-    activity_manager_write(request.pid, crash_signal, amfd, *amfd_data.get());
-  }
-
-  if (ptrace(PTRACE_DETACH, request.tid, 0, 0) != 0) {
-    ALOGE("debuggerd: ptrace detach from %d failed: %s", request.tid, strerror(errno));
-  }
-
-  for (pid_t sibling : siblings) {
-    ptrace(PTRACE_DETACH, sibling, 0, 0);
-  }
-
-  // Send the signal back to the process if it crashed and we're not waiting for gdb.
-  if (!attach_gdb && request.action == DEBUGGER_ACTION_CRASH) {
-    if (!send_signal(request.pid, request.tid, crash_signal)) {
-      ALOGE("debuggerd: failed to kill process %d: %s", request.pid, strerror(errno));
-    }
-  }
-
-  // Wait for gdb, if requested.
-  if (attach_gdb) {
-    wait_for_user_action(request);
-
-    // Now tell the activity manager about this process.
-    activity_manager_write(request.pid, crash_signal, amfd, *amfd_data.get());
-
-    // Tell the signal process to send SIGCONT to the target.
-    if (!send_signal(request.pid, 0, SIGCONT)) {
-      ALOGE("debuggerd: failed to resume process %d: %s", request.pid, strerror(errno));
-    }
-
-    uninit_getevent();
-  }
-
-  close(amfd);
-
-  exit(!succeeded);
-}
-
-static void monitor_worker_process(int child_pid, const debugger_request_t& request) {
-  struct timespec timeout = {.tv_sec = 10, .tv_nsec = 0 };
-  if (should_attach_gdb(request)) {
-    // If wait_for_gdb is enabled, set the timeout to something large.
-    timeout.tv_sec = INT_MAX;
-  }
-
-  sigset_t signal_set;
-  sigemptyset(&signal_set);
-  sigaddset(&signal_set, SIGCHLD);
-
-  bool kill_worker = false;
-  bool kill_target = false;
-  bool kill_self = false;
-
-  int status;
-  siginfo_t siginfo;
-  int signal = TEMP_FAILURE_RETRY(sigtimedwait(&signal_set, &siginfo, &timeout));
-  if (signal == SIGCHLD) {
-    pid_t rc = waitpid(-1, &status, WNOHANG | WUNTRACED);
-    if (rc != child_pid) {
-      ALOGE("debuggerd: waitpid returned unexpected pid (%d), committing murder-suicide", rc);
-
-      if (WIFEXITED(status)) {
-        ALOGW("debuggerd: pid %d exited with status %d", rc, WEXITSTATUS(status));
-      } else if (WIFSIGNALED(status)) {
-        ALOGW("debuggerd: pid %d received signal %d", rc, WTERMSIG(status));
-      } else if (WIFSTOPPED(status)) {
-        ALOGW("debuggerd: pid %d stopped by signal %d", rc, WSTOPSIG(status));
-      } else if (WIFCONTINUED(status)) {
-        ALOGW("debuggerd: pid %d continued", rc);
-      }
-
-      kill_worker = true;
-      kill_target = true;
-      kill_self = true;
-    } else if (WIFSIGNALED(status)) {
-      ALOGE("debuggerd: worker process %d terminated due to signal %d", child_pid, WTERMSIG(status));
-      kill_worker = false;
-      kill_target = true;
-    } else if (WIFSTOPPED(status)) {
-      ALOGE("debuggerd: worker process %d stopped due to signal %d", child_pid, WSTOPSIG(status));
-      kill_worker = true;
-      kill_target = true;
-    }
-  } else {
-    ALOGE("debuggerd: worker process %d timed out", child_pid);
-    kill_worker = true;
-    kill_target = true;
-  }
-
-  if (kill_worker) {
-    // Something bad happened, kill the worker.
-    if (kill(child_pid, SIGKILL) != 0) {
-      ALOGE("debuggerd: failed to kill worker process %d: %s", child_pid, strerror(errno));
-    } else {
-      waitpid(child_pid, &status, 0);
-    }
-  }
-
-  int exit_signal = SIGCONT;
-  if (kill_target && request.action == DEBUGGER_ACTION_CRASH) {
-    ALOGE("debuggerd: killing target %d", request.pid);
-    exit_signal = SIGKILL;
-  } else {
-    ALOGW("debuggerd: resuming target %d", request.pid);
-  }
-
-  if (kill(request.pid, exit_signal) != 0) {
-    ALOGE("debuggerd: failed to send signal %d to target: %s", exit_signal, strerror(errno));
-  }
-
-  if (kill_self) {
-    stop_signal_sender();
-    _exit(1);
-  }
-}
-
-static void handle_request(int fd) {
-  ALOGV("handle_request(%d)\n", fd);
-
-  android::base::unique_fd closer(fd);
-  debugger_request_t request;
-  memset(&request, 0, sizeof(request));
-  int status = read_request(fd, &request);
-  if (status != 0) {
-    return;
-  }
-
-  ALOGW("debuggerd: handling request: pid=%d uid=%d gid=%d tid=%d\n", request.pid, request.uid,
-        request.gid, request.tid);
-
-#if defined(__LP64__)
-  // On 64 bit systems, requests to dump 32 bit and 64 bit tids come
-  // to the 64 bit debuggerd. If the process is a 32 bit executable,
-  // redirect the request to the 32 bit debuggerd.
-  if (is32bit(request.tid)) {
-    // Only dump backtrace and dump tombstone requests can be redirected.
-    if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE ||
-        request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
-      redirect_to_32(fd, &request);
-    } else {
-      ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n", request.action);
-    }
-    return;
-  }
-#endif
-
-  // Fork a child to handle the rest of the request.
-  pid_t fork_pid = fork();
-  if (fork_pid == -1) {
-    ALOGE("debuggerd: failed to fork: %s\n", strerror(errno));
-  } else if (fork_pid == 0) {
-    worker_process(fd, request);
-  } else {
-    monitor_worker_process(fork_pid, request);
-  }
-}
-
-static int do_server() {
-  // debuggerd crashes can't be reported to debuggerd.
-  // Reset all of the crash handlers.
-  signal(SIGABRT, SIG_DFL);
-  signal(SIGBUS, SIG_DFL);
-  signal(SIGFPE, SIG_DFL);
-  signal(SIGILL, SIG_DFL);
-  signal(SIGSEGV, SIG_DFL);
-#ifdef SIGSTKFLT
-  signal(SIGSTKFLT, SIG_DFL);
-#endif
-  signal(SIGTRAP, SIG_DFL);
-
-  // Ignore failed writes to closed sockets
-  signal(SIGPIPE, SIG_IGN);
-
-  // Block SIGCHLD so we can sigtimedwait for it.
-  sigset_t sigchld;
-  sigemptyset(&sigchld);
-  sigaddset(&sigchld, SIGCHLD);
-  sigprocmask(SIG_SETMASK, &sigchld, nullptr);
-
-  int s = socket_local_server(SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
-                              SOCK_STREAM | SOCK_CLOEXEC);
-  if (s == -1) return 1;
-
-  // Fork a process that stays root, and listens on a pipe to pause and resume the target.
-  if (!start_signal_sender()) {
-    ALOGE("debuggerd: failed to fork signal sender");
-    return 1;
-  }
-
-  ALOGI("debuggerd: starting\n");
-
-  for (;;) {
-    ALOGV("waiting for connection\n");
-    int fd = accept4(s, nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK);
-    if (fd == -1) {
-      ALOGE("accept failed: %s\n", strerror(errno));
-      continue;
-    }
-
-    handle_request(fd);
-  }
-  return 0;
-}
-
-static int do_explicit_dump(pid_t tid, bool dump_backtrace) {
-  fprintf(stdout, "Sending request to dump task %d...\n", tid);
-  fflush(stdout);
-
-  // TODO: we could have better error reporting if debuggerd sent an error string back.
-  if (dump_backtrace) {
-    if (dump_backtrace_to_file(tid, fileno(stdout)) < 0) {
-      fputs("Error dumping backtrace (check logcat).\n", stderr);
-      return 1;
-    }
-  } else {
-    char tombstone_path[PATH_MAX];
-    if (dump_tombstone(tid, tombstone_path, sizeof(tombstone_path)) < 0) {
-      fputs("Error dumping tombstone (check logcat).\n", stderr);
-      return 1;
-    }
-    fprintf(stderr, "Tombstone written to: %s\n", tombstone_path);
-  }
-  return 0;
-}
-
-static int usage() {
-  fputs("usage: debuggerd [-b] [<tid>]\n"
-        "\n"
-        "Given a thread id, sends a request to debuggerd to dump that thread.\n"
-        "Otherwise, starts the debuggerd server.\n"
-        "\n"
-        "-b\tdump backtrace to console, otherwise generate tombstone\n", stderr);
-  return EXIT_FAILURE;
-}
-
-int main(int argc, char** argv) {
-  union selinux_callback cb;
-  if (argc == 1) {
-    cb.func_audit = audit_callback;
-    selinux_set_callback(SELINUX_CB_AUDIT, cb);
-    cb.func_log = selinux_log_callback;
-    selinux_set_callback(SELINUX_CB_LOG, cb);
-    return do_server();
-  }
-
-  bool dump_backtrace = false;
-  pid_t tid = 0;
-  for (int i = 1; i < argc; i++) {
-    if (!strcmp(argv[i], "-b")) {
-      dump_backtrace = true;
-    } else if (tid != 0 || (tid = atoi(argv[i])) == 0) {
-      // Only one tid is allowed. (And 0 isn't a valid tid.)
-      // atoi(3) returns 0 on failure to parse, so this catches anything else too.
-      return usage();
-    }
-  }
-  return (tid != 0) ? do_explicit_dump(tid, dump_backtrace) : usage();
-}
diff --git a/debuggerd/debuggerd.rc b/debuggerd/debuggerd.rc
deleted file mode 100644
index 1c6b9ff..0000000
--- a/debuggerd/debuggerd.rc
+++ /dev/null
@@ -1,3 +0,0 @@
-service debuggerd /system/bin/debuggerd
-    group root readproc
-    writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/debuggerd64.rc b/debuggerd/debuggerd64.rc
deleted file mode 100644
index 3e8847a..0000000
--- a/debuggerd/debuggerd64.rc
+++ /dev/null
@@ -1,3 +0,0 @@
-service debuggerd64 /system/bin/debuggerd64
-    group root readproc
-    writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
new file mode 100644
index 0000000..1a27f3f
--- /dev/null
+++ b/debuggerd/debuggerd_test.cpp
@@ -0,0 +1,510 @@
+/*
+ * Copyright 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 <err.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+
+#include <chrono>
+#include <regex>
+#include <thread>
+
+#include <android/set_abort_message.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#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 <gtest/gtest.h>
+
+using namespace std::chrono_literals;
+using android::base::unique_fd;
+
+#if defined(__LP64__)
+#define ARCH_SUFFIX "64"
+#else
+#define ARCH_SUFFIX ""
+#endif
+
+constexpr char kWaitForGdbKey[] = "debug.debuggerd.wait_for_gdb";
+
+#define TIMEOUT(seconds, expr)                                     \
+  [&]() {                                                          \
+    struct sigaction old_sigaction;                                \
+    struct sigaction new_sigaction = {};                           \
+    new_sigaction.sa_handler = [](int) {};                         \
+    if (sigaction(SIGALRM, &new_sigaction, &new_sigaction) != 0) { \
+      err(1, "sigaction failed");                                  \
+    }                                                              \
+    alarm(seconds);                                                \
+    auto value = expr;                                             \
+    int saved_errno = errno;                                       \
+    if (sigaction(SIGALRM, &old_sigaction, nullptr) != 0) {        \
+      err(1, "sigaction failed");                                  \
+    }                                                              \
+    alarm(0);                                                      \
+    errno = saved_errno;                                           \
+    return value;                                                  \
+  }()
+
+#define ASSERT_MATCH(str, pattern)                                              \
+  do {                                                                          \
+    std::regex r((pattern));                                                    \
+    if (!std::regex_search((str), r)) {                                         \
+      FAIL() << "regex mismatch: expected " << (pattern) << " in: \n" << (str); \
+    }                                                                           \
+  } while (0)
+
+class CrasherTest : public ::testing::Test {
+ public:
+  pid_t crasher_pid = -1;
+  bool previous_wait_for_gdb;
+  unique_fd crasher_pipe;
+  unique_fd intercept_fd;
+
+  CrasherTest();
+  ~CrasherTest();
+
+  void StartIntercept(unique_fd* output_fd);
+
+  // Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
+  void FinishIntercept(int* result);
+
+  void StartProcess(std::function<void()> function);
+  void StartCrasher(const std::string& crash_type);
+  void FinishCrasher();
+  void AssertDeath(int signo);
+};
+
+CrasherTest::CrasherTest() {
+  previous_wait_for_gdb = android::base::GetBoolProperty(kWaitForGdbKey, false);
+  android::base::SetProperty(kWaitForGdbKey, "0");
+}
+
+CrasherTest::~CrasherTest() {
+  if (crasher_pid != -1) {
+    kill(crasher_pid, SIGKILL);
+    int status;
+    waitpid(crasher_pid, &status, WUNTRACED);
+  }
+
+  android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
+}
+
+void CrasherTest::StartIntercept(unique_fd* output_fd) {
+  if (crasher_pid == -1) {
+    FAIL() << "crasher hasn't been started";
+  }
+
+  intercept_fd.reset(socket_local_client(kTombstonedInterceptSocketName,
+                                         ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+  if (intercept_fd == -1) {
+    FAIL() << "failed to contact tombstoned: " << strerror(errno);
+  }
+
+  InterceptRequest req = {.pid = crasher_pid };
+
+  unique_fd output_pipe_write;
+  if (!Pipe(output_fd, &output_pipe_write)) {
+    FAIL() << "failed to create output pipe: " << strerror(errno);
+  }
+
+  std::string pipe_size_str;
+  int pipe_buffer_size;
+  if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
+    FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
+  }
+
+  pipe_size_str = android::base::Trim(pipe_size_str);
+
+  if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
+    FAIL() << "failed to parse pipe max size";
+  }
+
+  if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
+    FAIL() << "failed to set pipe size: " << strerror(errno);
+  }
+
+  if (send_fd(intercept_fd.get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
+    FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
+  }
+}
+
+void CrasherTest::FinishIntercept(int* result) {
+  InterceptResponse response;
+
+  // Timeout for tombstoned intercept is 10 seconds.
+  ssize_t rc = TIMEOUT(20, read(intercept_fd.get(), &response, sizeof(response)));
+  if (rc == -1) {
+    FAIL() << "failed to read response from tombstoned: " << strerror(errno);
+  } else if (rc == 0) {
+    *result = -1;
+  } else if (rc != sizeof(response)) {
+    FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
+           << ", received " << rc;
+  } else {
+    *result = response.success;
+  }
+}
+
+void CrasherTest::StartProcess(std::function<void()> function) {
+  unique_fd read_pipe;
+  unique_fd crasher_read_pipe;
+  if (!Pipe(&crasher_read_pipe, &crasher_pipe)) {
+    FAIL() << "failed to create pipe: " << strerror(errno);
+  }
+
+  crasher_pid = fork();
+  if (crasher_pid == -1) {
+    FAIL() << "fork failed: " << strerror(errno);
+  } else if (crasher_pid == 0) {
+    char dummy;
+    crasher_pipe.reset();
+    TEMP_FAILURE_RETRY(read(crasher_read_pipe.get(), &dummy, 1));
+    function();
+    _exit(0);
+  }
+}
+
+void CrasherTest::FinishCrasher() {
+  if (crasher_pipe == -1) {
+    FAIL() << "crasher pipe uninitialized";
+  }
+
+  ssize_t rc = write(crasher_pipe.get(), "\n", 1);
+  if (rc == -1) {
+    FAIL() << "failed to write to crasher pipe: " << strerror(errno);
+  } else if (rc == 0) {
+    FAIL() << "crasher pipe was closed";
+  }
+}
+
+void CrasherTest::AssertDeath(int signo) {
+  int status;
+  pid_t pid = TIMEOUT(5, waitpid(crasher_pid, &status, 0));
+  if (pid != crasher_pid) {
+    FAIL() << "failed to wait for crasher: " << strerror(errno);
+  }
+
+  if (WIFEXITED(status)) {
+    FAIL() << "crasher failed to exec: " << strerror(WEXITSTATUS(status));
+  } else if (!WIFSIGNALED(status)) {
+    FAIL() << "crasher didn't terminate via a signal";
+  }
+  ASSERT_EQ(signo, WTERMSIG(status));
+  crasher_pid = -1;
+}
+
+static void ConsumeFd(unique_fd fd, std::string* output) {
+  constexpr size_t read_length = PAGE_SIZE;
+  std::string result;
+
+  while (true) {
+    size_t offset = result.size();
+    result.resize(result.size() + PAGE_SIZE);
+    ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &result[offset], read_length));
+    if (rc == -1) {
+      FAIL() << "read failed: " << strerror(errno);
+    } else if (rc == 0) {
+      result.resize(result.size() - PAGE_SIZE);
+      break;
+    }
+
+    result.resize(result.size() - PAGE_SIZE + rc);
+  }
+
+  *output = std::move(result);
+}
+
+TEST_F(CrasherTest, smoke) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() {
+    *reinterpret_cast<volatile char*>(0xdead) = '1';
+  });
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
+}
+
+TEST_F(CrasherTest, abort) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() {
+    abort();
+  });
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  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)");
+}
+
+TEST_F(CrasherTest, signal) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() {
+    abort();
+  });
+  StartIntercept(&output_fd);
+
+  // Wait for a bit, or we might end up killing the process before the signal
+  // handler even gets a chance to be registered.
+  std::this_thread::sleep_for(100ms);
+  ASSERT_EQ(0, kill(crasher_pid, SIGSEGV));
+
+  AssertDeath(SIGSEGV);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 0 \(SI_USER\), fault addr --------)");
+  ASSERT_MATCH(result, R"(backtrace:)");
+}
+
+TEST_F(CrasherTest, abort_message) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() {
+    android_set_abort_message("abort message goes here");
+    abort();
+  });
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(Abort message: 'abort message goes here')");
+}
+
+TEST_F(CrasherTest, intercept_timeout) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() {
+    abort();
+  });
+  StartIntercept(&output_fd);
+
+  // Don't let crasher finish until we timeout.
+  FinishIntercept(&intercept_result);
+
+  ASSERT_NE(1, intercept_result) << "tombstoned reported success? (intercept_result = "
+                                 << intercept_result << ")";
+
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+}
+
+TEST_F(CrasherTest, wait_for_gdb) {
+  if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
+    FAIL() << "failed to enable wait_for_gdb";
+  }
+  sleep(1);
+
+  StartProcess([]() {
+    abort();
+  });
+  FinishCrasher();
+
+  int status;
+  ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, WUNTRACED));
+  ASSERT_TRUE(WIFSTOPPED(status));
+  ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
+
+  ASSERT_EQ(0, kill(crasher_pid, SIGCONT));
+
+  AssertDeath(SIGABRT);
+}
+
+// wait_for_gdb shouldn't trigger on manually sent signals.
+TEST_F(CrasherTest, wait_for_gdb_signal) {
+  if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
+    FAIL() << "failed to enable wait_for_gdb";
+  }
+
+  StartProcess([]() {
+    abort();
+  });
+  ASSERT_EQ(0, kill(crasher_pid, SIGSEGV)) << strerror(errno);
+  AssertDeath(SIGSEGV);
+}
+
+TEST_F(CrasherTest, backtrace) {
+  std::string result;
+  int intercept_result;
+  unique_fd output_fd;
+
+  StartProcess([]() {
+    abort();
+  });
+  StartIntercept(&output_fd);
+
+  std::this_thread::sleep_for(500ms);
+
+  sigval val;
+  val.sival_int = 1;
+  ASSERT_EQ(0, sigqueue(crasher_pid, DEBUGGER_SIGNAL, val)) << strerror(errno);
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+  /system/lib)" ARCH_SUFFIX R"(/libc.so \(read\+)");
+
+  int status;
+  ASSERT_EQ(0, waitpid(crasher_pid, &status, WNOHANG | WUNTRACED));
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  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)");
+}
+
+TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() {
+    prctl(PR_SET_DUMPABLE, 0);
+    abort();
+  });
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  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)");
+}
+
+TEST_F(CrasherTest, capabilities) {
+  ASSERT_EQ(0U, getuid()) << "capability test requires root";
+
+  StartProcess([]() {
+    if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0) {
+      err(1, "failed to set PR_SET_KEEPCAPS");
+    }
+
+    if (setresuid(1, 1, 1) != 0) {
+      err(1, "setresuid failed");
+    }
+
+    __user_cap_header_struct capheader;
+    __user_cap_data_struct capdata[2];
+    memset(&capheader, 0, sizeof(capheader));
+    memset(&capdata, 0, sizeof(capdata));
+
+    capheader.version = _LINUX_CAPABILITY_VERSION_3;
+    capheader.pid = 0;
+
+    // Turn on every third capability.
+    static_assert(CAP_LAST_CAP > 33, "CAP_LAST_CAP <= 32");
+    for (int i = 0; i < CAP_LAST_CAP; i += 3) {
+      capdata[CAP_TO_INDEX(i)].permitted |= CAP_TO_MASK(i);
+      capdata[CAP_TO_INDEX(i)].effective |= CAP_TO_MASK(i);
+    }
+
+    // Make sure CAP_SYS_PTRACE is off.
+    capdata[CAP_TO_INDEX(CAP_SYS_PTRACE)].permitted &= ~(CAP_TO_MASK(CAP_SYS_PTRACE));
+    capdata[CAP_TO_INDEX(CAP_SYS_PTRACE)].effective &= ~(CAP_TO_MASK(CAP_SYS_PTRACE));
+
+    if (capset(&capheader, &capdata[0]) != 0) {
+      err(1, "capset failed");
+    }
+
+    if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) != 0) {
+      err(1, "failed to drop ambient capabilities");
+    }
+
+    raise(SIGSYS);
+  });
+
+  unique_fd output_fd;
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSYS);
+
+  std::string result;
+  int intercept_result;
+  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)");
+}
+
+TEST(crash_dump, zombie) {
+  pid_t forkpid = fork();
+
+  int pipefd[2];
+  ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
+
+  pid_t rc;
+  int status;
+
+  if (forkpid == 0) {
+    errno = 0;
+    rc = waitpid(-1, &status, WNOHANG | __WALL | __WNOTHREAD);
+    if (rc != -1 || errno != ECHILD) {
+      errx(2, "first waitpid returned %d (%s), expected failure with ECHILD", rc, strerror(errno));
+    }
+
+    raise(DEBUGGER_SIGNAL);
+
+    errno = 0;
+    rc = waitpid(-1, &status, __WALL | __WNOTHREAD);
+    if (rc != -1 || errno != ECHILD) {
+      errx(2, "second waitpid returned %d (%s), expected failure with ECHILD", rc, strerror(errno));
+    }
+    _exit(0);
+  } else {
+    rc = waitpid(forkpid, &status, 0);
+    ASSERT_EQ(forkpid, rc);
+    ASSERT_TRUE(WIFEXITED(status));
+    ASSERT_EQ(0, WEXITSTATUS(status));
+  }
+}
diff --git a/debuggerd/getevent.cpp b/debuggerd/getevent.cpp
deleted file mode 100644
index dbb878a..0000000
--- a/debuggerd/getevent.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * 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.
- */
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/input.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/inotify.h>
-#include <sys/ioctl.h>
-#include <sys/limits.h>
-#include <sys/poll.h>
-#include <unistd.h>
-
-#include <memory>
-
-#include <android/log.h>
-
-static struct pollfd* ufds;
-static char** device_names;
-static int nfds;
-
-static int open_device(const char* device) {
-  int version;
-  int fd;
-  struct pollfd* new_ufds;
-  char** new_device_names;
-  char name[80];
-  char location[80];
-  char idstr[80];
-  struct input_id id;
-
-  fd = open(device, O_RDWR);
-  if (fd < 0) {
-    return -1;
-  }
-
-  if (ioctl(fd, EVIOCGVERSION, &version)) {
-    return -1;
-  }
-  if (ioctl(fd, EVIOCGID, &id)) {
-    return -1;
-  }
-  name[sizeof(name) - 1] = '\0';
-  location[sizeof(location) - 1] = '\0';
-  idstr[sizeof(idstr) - 1] = '\0';
-  if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
-    name[0] = '\0';
-  }
-  if (ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
-    location[0] = '\0';
-  }
-  if (ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
-    idstr[0] = '\0';
-  }
-
-  new_ufds = reinterpret_cast<pollfd*>(realloc(ufds, sizeof(ufds[0]) * (nfds + 1)));
-  if (new_ufds == NULL) {
-    fprintf(stderr, "out of memory\n");
-    return -1;
-  }
-  ufds = new_ufds;
-  new_device_names = reinterpret_cast<char**>(realloc(
-      device_names, sizeof(device_names[0]) * (nfds + 1)));
-  if (new_device_names == NULL) {
-    fprintf(stderr, "out of memory\n");
-    return -1;
-  }
-  device_names = new_device_names;
-  ufds[nfds].fd = fd;
-  ufds[nfds].events = POLLIN;
-  device_names[nfds] = strdup(device);
-  nfds++;
-
-  return 0;
-}
-
-int close_device(const char* device) {
-  int i;
-  for (i = 1; i < nfds; i++) {
-    if (strcmp(device_names[i], device) == 0) {
-      int count = nfds - i - 1;
-      free(device_names[i]);
-      memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);
-      memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);
-      nfds--;
-      return 0;
-    }
-  }
-  return -1;
-}
-
-static int read_notify(const char* dirname, int nfd) {
-  int res;
-  char devname[PATH_MAX];
-  char* filename;
-  char event_buf[512];
-  int event_size;
-  int event_pos = 0;
-  struct inotify_event *event;
-
-  res = read(nfd, event_buf, sizeof(event_buf));
-  if (res < (int)sizeof(*event)) {
-    if (errno == EINTR)
-      return 0;
-    fprintf(stderr, "could not get event, %s\n", strerror(errno));
-    return 1;
-  }
-
-  strcpy(devname, dirname);
-  filename = devname + strlen(devname);
-  *filename++ = '/';
-
-  while (res >= (int)sizeof(*event)) {
-    event = reinterpret_cast<struct inotify_event*>(event_buf + event_pos);
-    if (event->len) {
-      strcpy(filename, event->name);
-      if (event->mask & IN_CREATE) {
-        open_device(devname);
-      } else {
-        close_device(devname);
-      }
-    }
-    event_size = sizeof(*event) + event->len;
-    res -= event_size;
-    event_pos += event_size;
-  }
-  return 0;
-}
-
-static int scan_dir(const char* dirname) {
-  char devname[PATH_MAX];
-  char* filename;
-  struct dirent* de;
-  std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
-  if (dir == NULL)
-    return -1;
-  strcpy(devname, dirname);
-  filename = devname + strlen(devname);
-  *filename++ = '/';
-  while ((de = readdir(dir.get()))) {
-    if ((de->d_name[0] == '.' && de->d_name[1] == '\0') ||
-        (de->d_name[1] == '.' && de->d_name[2] == '\0'))
-      continue;
-    strcpy(filename, de->d_name);
-    open_device(devname);
-  }
-  return 0;
-}
-
-int init_getevent() {
-  int res;
-  const char* device_path = "/dev/input";
-
-  nfds = 1;
-  ufds = reinterpret_cast<pollfd*>(calloc(1, sizeof(ufds[0])));
-  ufds[0].fd = inotify_init();
-  ufds[0].events = POLLIN;
-
-  res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
-  if (res < 0) {
-    return 1;
-  }
-  res = scan_dir(device_path);
-  if (res < 0) {
-    return 1;
-  }
-  return 0;
-}
-
-void uninit_getevent() {
-  int i;
-  for (i = 0; i < nfds; i++) {
-    close(ufds[i].fd);
-  }
-  free(ufds);
-  ufds = 0;
-  nfds = 0;
-}
-
-int get_event(struct input_event* event, int timeout) {
-  int res;
-  int i;
-  int pollres;
-  const char* device_path = "/dev/input";
-  while (1) {
-    pollres = poll(ufds, nfds, timeout);
-    if (pollres == 0) {
-      return 1;
-    }
-    if (ufds[0].revents & POLLIN) {
-      read_notify(device_path, ufds[0].fd);
-    }
-    for (i = 1; i < nfds; i++) {
-      if (ufds[i].revents) {
-        if (ufds[i].revents & POLLIN) {
-          res = read(ufds[i].fd, event, sizeof(*event));
-          if (res < static_cast<int>(sizeof(event))) {
-            fprintf(stderr, "could not get event\n");
-            return -1;
-          }
-          return 0;
-        }
-      }
-    }
-  }
-  return 0;
-}
diff --git a/debuggerd/getevent.h b/debuggerd/getevent.h
deleted file mode 100644
index 426139d..0000000
--- a/debuggerd/getevent.h
+++ /dev/null
@@ -1,24 +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 _DEBUGGERD_GETEVENT_H
-#define _DEBUGGERD_GETEVENT_H
-
-int init_getevent();
-void uninit_getevent();
-int get_event(struct input_event* event, int timeout);
-
-#endif // _DEBUGGERD_GETEVENT_H
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
new file mode 100644
index 0000000..77ad6ac
--- /dev/null
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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 <stddef.h>
+#include <sys/ucontext.h>
+#include <unistd.h>
+
+#include "tombstone.h"
+
+extern "C" void __linker_use_fallback_allocator();
+
+extern "C" bool debuggerd_fallback(ucontext_t* ucontext, siginfo_t* siginfo, void* abort_message) {
+  // This is incredibly sketchy to do inside of a signal handler, especially when libbacktrace
+  // uses the C++ standard library throughout, but this code runs in the linker, so we'll be using
+  // the linker's malloc instead of the libc one. Switch it out for a replacement, just in case.
+  //
+  // This isn't the default method of dumping because it can fail in cases such as memory space
+  // exhaustion.
+  __linker_use_fallback_allocator();
+  engrave_tombstone_ucontext(-1, getpid(), gettid(), reinterpret_cast<uintptr_t>(abort_message),
+                             siginfo, ucontext);
+  return true;
+}
diff --git a/debuggerd/test/sys/system_properties.h b/debuggerd/handler/debuggerd_fallback_nop.cpp
similarity index 79%
copy from debuggerd/test/sys/system_properties.h
copy to debuggerd/handler/debuggerd_fallback_nop.cpp
index 9d44345..9b3053f 100644
--- a/debuggerd/test/sys/system_properties.h
+++ b/debuggerd/handler/debuggerd_fallback_nop.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,13 +26,10 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
-#define _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
+#include <stddef.h>
+#include <sys/ucontext.h>
+#include <unistd.h>
 
-// This is just enough to get the property code to compile on
-// the host.
-
-#define PROP_NAME_MAX   32
-#define PROP_VALUE_MAX  92
-
-#endif // _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
+extern "C" bool debuggerd_fallback(ucontext_t*, siginfo_t*, void*) {
+  return false;
+}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
new file mode 100644
index 0000000..67c26e2
--- /dev/null
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "debuggerd/handler.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/futex.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "private/bionic_futex.h"
+#include "private/libc_logging.h"
+
+// see man(2) prctl, specifically the section about PR_GET_NAME
+#define MAX_TASK_NAME_LEN (16)
+
+#if defined(__LP64__)
+#define CRASH_DUMP_NAME "crash_dump64"
+#else
+#define CRASH_DUMP_NAME "crash_dump32"
+#endif
+
+#define CRASH_DUMP_PATH "/system/bin/" CRASH_DUMP_NAME
+
+extern "C" bool debuggerd_fallback(ucontext_t*, siginfo_t*, void*);
+
+static debuggerd_callbacks_t g_callbacks;
+
+// 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.
+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);
+  _exit(1);
+}
+
+static void __noreturn __printflike(1, 2) fatal_errno(const char* fmt, ...) {
+  int err = errno;
+  va_list args;
+  va_start(args, fmt);
+
+  char buf[4096];
+  __libc_format_buffer_va_list(buf, sizeof(buf), fmt, args);
+  fatal("%s: %s", buf, strerror(err));
+}
+
+/*
+ * 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
+ * indication of the failure in the log.
+ *
+ * We could be here as a result of native heap corruption, or while a
+ * mutex is being held, so we don't want to use any libc functions that
+ * could allocate memory or hold a lock.
+ */
+static void log_signal_summary(int signum, const siginfo_t* info) {
+  char thread_name[MAX_TASK_NAME_LEN + 1];  // one more for termination
+  if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
+    strcpy(thread_name, "<name unknown>");
+  } else {
+    // short names are null terminated by prctl, but the man page
+    // implies that 16 byte names are not.
+    thread_name[MAX_TASK_NAME_LEN] = 0;
+  }
+
+  if (signum == DEBUGGER_SIGNAL) {
+    __libc_format_log(ANDROID_LOG_FATAL, "libc", "Requested dump for tid %d (%s)", gettid(),
+                      thread_name);
+    return;
+  }
+
+  const char* signal_name = "???";
+  bool has_address = false;
+  switch (signum) {
+    case SIGABRT:
+      signal_name = "SIGABRT";
+      break;
+    case SIGBUS:
+      signal_name = "SIGBUS";
+      has_address = true;
+      break;
+    case SIGFPE:
+      signal_name = "SIGFPE";
+      has_address = true;
+      break;
+    case SIGILL:
+      signal_name = "SIGILL";
+      has_address = true;
+      break;
+    case SIGSEGV:
+      signal_name = "SIGSEGV";
+      has_address = true;
+      break;
+#if defined(SIGSTKFLT)
+    case SIGSTKFLT:
+      signal_name = "SIGSTKFLT";
+      break;
+#endif
+    case SIGSYS:
+      signal_name = "SIGSYS";
+      break;
+    case SIGTRAP:
+      signal_name = "SIGTRAP";
+      break;
+  }
+
+  // "info" will be null if the siginfo_t information was not available.
+  // Many signals don't have an address or a code.
+  char code_desc[32];  // ", code -6"
+  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);
+    if (has_address) {
+      __libc_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);
+}
+
+/*
+ * Returns true if the handler for signal "signum" has SA_SIGINFO set.
+ */
+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));
+    return false;
+  }
+  return (old_action.sa_flags & SA_SIGINFO) != 0;
+}
+
+static void raise_caps() {
+  // Raise CapInh to match CapPrm, so that we can set the ambient bits.
+  __user_cap_header_struct capheader;
+  memset(&capheader, 0, sizeof(capheader));
+  capheader.version = _LINUX_CAPABILITY_VERSION_3;
+  capheader.pid = 0;
+
+  __user_cap_data_struct capdata[2];
+  if (capget(&capheader, &capdata[0]) == -1) {
+    fatal_errno("capget failed");
+  }
+
+  if (capdata[0].permitted != capdata[0].inheritable ||
+      capdata[1].permitted != capdata[1].inheritable) {
+    capdata[0].inheritable = capdata[0].permitted;
+    capdata[1].inheritable = capdata[1].permitted;
+
+    if (capset(&capheader, &capdata[0]) == -1) {
+      __libc_format_log(ANDROID_LOG_ERROR, "libc", "capset failed: %s", strerror(errno));
+    }
+  }
+
+  // Set the ambient capability bits so that crash_dump gets all of our caps and can ptrace us.
+  uint64_t capmask = capdata[0].inheritable;
+  capmask |= static_cast<uint64_t>(capdata[1].inheritable) << 32;
+  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));
+      }
+    }
+  }
+}
+
+struct debugger_thread_info {
+  bool crash_dump_started;
+  pid_t crashing_tid;
+  pid_t pseudothread_tid;
+  int signal_number;
+  siginfo_t* info;
+};
+
+// Logging and contacting debuggerd requires free file descriptors, which we might not have.
+// Work around this by spawning a "thread" that shares its parent's address space, but not its file
+// descriptor table, so that we can close random file descriptors without affecting the original
+// process. Note that this doesn't go through pthread_create, so TLS is shared with the spawning
+// process.
+static void* pseudothread_stack;
+
+static int debuggerd_dispatch_pseudothread(void* arg) {
+  debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);
+
+  for (int i = 0; i < 1024; ++i) {
+    close(i);
+  }
+
+  int devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
+
+  // devnull will be 0.
+  TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO));
+  TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO));
+
+  int pipefds[2];
+  if (pipe(pipefds) != 0) {
+    fatal_errno("failed to create pipe");
+  }
+
+  // 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));
+  } else if (forkpid == 0) {
+    TEMP_FAILURE_RETRY(dup2(pipefds[1], STDOUT_FILENO));
+    close(pipefds[0]);
+    close(pipefds[1]);
+
+    raise_caps();
+
+    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);
+
+    execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, nullptr);
+
+    fatal_errno("exec failed");
+  } else {
+    close(pipefds[1]);
+    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));
+    } else if (rc == 0) {
+      __libc_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);
+    } else {
+      if (buf[0] != '\1') {
+        __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
+      } else {
+        thread_info->crash_dump_started = true;
+      }
+    }
+    close(pipefds[0]);
+
+    // 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));
+    } else if (WIFSTOPPED(status) || WIFSIGNALED(status)) {
+      __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
+      thread_info->crash_dump_started = false;
+    }
+  }
+
+  syscall(__NR_exit, 0);
+  return 0;
+}
+
+static void resend_signal(siginfo_t* info, bool crash_dump_started) {
+  // Signals can either be fatal or nonfatal.
+  // For fatal signals, crash_dump will send us the signal we crashed with
+  // before resuming us, so that processes using waitpid on us will see that we
+  // exited with the correct exit status (e.g. so that sh will report
+  // "Segmentation fault" instead of "Killed"). For this to work, we need
+  // to deregister our signal handler for that signal before continuing.
+  if (info->si_signo != DEBUGGER_SIGNAL) {
+    signal(info->si_signo, SIG_DFL);
+  }
+
+  // We need to return from our signal handler so that crash_dump can see the
+  // signal via ptrace and dump the thread that crashed. However, returning
+  // does not guarantee that the signal will be thrown again, even for SIGSEGV
+  // and friends, since the signal could have been sent manually. We blocked
+  // all signals when registering the handler, so resending the signal (using
+  // rt_tgsigqueueinfo(2) to preserve SA_SIGINFO) will cause it to be delivered
+  // when our signal handler returns.
+  if (crash_dump_started || info->si_signo != DEBUGGER_SIGNAL) {
+    int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), info->si_signo, info);
+    if (rc != 0) {
+      fatal_errno("failed to resend signal during crash");
+    }
+  }
+
+  if (info->si_signo == DEBUGGER_SIGNAL) {
+    pthread_mutex_unlock(&crash_mutex);
+  }
+}
+
+// Handler that does crash dumping by forking and doing the processing in the child.
+// Do this by ptracing the relevant thread, and then execing debuggerd to do the actual dump.
+static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* context) {
+  int ret = pthread_mutex_lock(&crash_mutex);
+  if (ret != 0) {
+    __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
+    return;
+  }
+
+  // It's possible somebody cleared the SA_SIGINFO flag, which would mean
+  // our "info" arg holds an undefined value.
+  if (!have_siginfo(signal_number)) {
+    info = nullptr;
+  }
+
+  struct siginfo si = {};
+  if (!info) {
+    memset(&si, 0, sizeof(si));
+    si.si_signo = signal_number;
+    si.si_code = SI_USER;
+    si.si_pid = getpid();
+    si.si_uid = getuid();
+    info = &si;
+  } else if (info->si_code >= 0 || info->si_code == SI_TKILL) {
+    // rt_tgsigqueueinfo(2)'s documentation appears to be incorrect on kernels
+    // that contain commit 66dd34a (3.9+). The manpage claims to only allow
+    // negative si_code values that are not SI_TKILL, but 66dd34a changed the
+    // check to allow all si_code values in calls coming from inside the house.
+  }
+
+  log_signal_summary(signal_number, info);
+
+  void* abort_message = nullptr;
+  if (g_callbacks.get_abort_message) {
+    abort_message = g_callbacks.get_abort_message();
+  }
+
+  if (prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
+    ucontext_t* ucontext = static_cast<ucontext_t*>(context);
+    if (signal_number == DEBUGGER_SIGNAL || !debuggerd_fallback(ucontext, info, abort_message)) {
+      // The process has NO_NEW_PRIVS enabled, so we can't transition to the crash_dump context.
+      __libc_format_log(ANDROID_LOG_INFO, "libc",
+                        "Suppressing debuggerd output because prctl(PR_GET_NO_NEW_PRIVS)==1");
+    }
+    resend_signal(info, false);
+    return;
+  }
+
+  // Populate si_value with the abort message address, if found.
+  if (abort_message) {
+    info->si_value.sival_ptr = abort_message;
+  }
+
+  debugger_thread_info thread_info = {
+    .crash_dump_started = false,
+    .pseudothread_tid = -1,
+    .crashing_tid = gettid(),
+    .signal_number = signal_number,
+    .info = info
+  };
+
+  // Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us.
+  int orig_dumpable = prctl(PR_GET_DUMPABLE);
+  if (prctl(PR_SET_DUMPABLE, 1) != 0) {
+    fatal_errno("failed to set dumpable");
+  }
+
+  // Essentially pthread_create without CLONE_FILES (see debuggerd_dispatch_pseudothread).
+  pid_t child_pid =
+    clone(debuggerd_dispatch_pseudothread, pseudothread_stack,
+          CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID,
+          &thread_info, nullptr, nullptr, &thread_info.pseudothread_tid);
+  if (child_pid == -1) {
+    fatal_errno("failed to spawn debuggerd dispatch thread");
+  }
+
+  // Wait for the child to start...
+  __futex_wait(&thread_info.pseudothread_tid, -1, nullptr);
+
+  // and then wait for it to finish.
+  __futex_wait(&thread_info.pseudothread_tid, child_pid, nullptr);
+
+  // Restore PR_SET_DUMPABLE to its original value.
+  if (prctl(PR_SET_DUMPABLE, orig_dumpable) != 0) {
+    fatal_errno("failed to restore dumpable");
+  }
+
+  // Signals can either be fatal or nonfatal.
+  // For fatal signals, crash_dump will PTRACE_CONT us with the signal we
+  // crashed with, so that processes using waitpid on us will see that we
+  // exited with the correct exit status (e.g. so that sh will report
+  // "Segmentation fault" instead of "Killed"). For this to work, we need
+  // to deregister our signal handler for that signal before continuing.
+  if (signal_number != DEBUGGER_SIGNAL) {
+    signal(signal_number, SIG_DFL);
+  }
+
+  resend_signal(info, thread_info.crash_dump_started);
+}
+
+void debuggerd_init(debuggerd_callbacks_t* callbacks) {
+  if (callbacks) {
+    g_callbacks = *callbacks;
+  }
+
+  void* thread_stack_allocation =
+    mmap(nullptr, PAGE_SIZE * 3, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  if (thread_stack_allocation == MAP_FAILED) {
+    fatal_errno("failed to allocate debuggerd thread stack");
+  }
+
+  char* stack = static_cast<char*>(thread_stack_allocation) + PAGE_SIZE;
+  if (mprotect(stack, PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) {
+    fatal_errno("failed to mprotect debuggerd thread stack");
+  }
+
+  // Stack grows negatively, set it to the last byte in the page...
+  stack = (stack + PAGE_SIZE - 1);
+  // and align it.
+  stack -= 15;
+  pseudothread_stack = stack;
+
+  struct sigaction action;
+  memset(&action, 0, sizeof(action));
+  sigfillset(&action.sa_mask);
+  action.sa_sigaction = debuggerd_signal_handler;
+  action.sa_flags = SA_RESTART | SA_SIGINFO;
+
+  // Use the alternate signal stack if available so we can catch stack overflows.
+  action.sa_flags |= SA_ONSTACK;
+  debuggerd_register_handlers(&action);
+}
diff --git a/debuggerd/include/debuggerd/client.h b/debuggerd/include/debuggerd/client.h
index aeb723b..91f143b 100644
--- a/debuggerd/include/debuggerd/client.h
+++ b/debuggerd/include/debuggerd/client.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 The Android Open Source Project
+ * Copyright 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.
@@ -16,44 +16,21 @@
 
 #pragma once
 
-#include <stdint.h>
+#include <stdbool.h>
 #include <sys/cdefs.h>
-#include <sys/types.h>
+#include <unistd.h>
 
-// On 32-bit devices, DEBUGGER_SOCKET_NAME is a 32-bit debuggerd.
-// On 64-bit devices, DEBUGGER_SOCKET_NAME is a 64-bit debuggerd.
-#define DEBUGGER_SOCKET_NAME "android:debuggerd"
+#include <android-base/unique_fd.h>
 
-// Used only on 64-bit devices for debuggerd32.
-#define DEBUGGER32_SOCKET_NAME "android:debuggerd32"
+enum DebuggerdDumpType {
+  kDebuggerdBacktrace,
+  kDebuggerdTombstone,
+};
 
-__BEGIN_DECLS
+// Trigger a dump of specified process to output_fd.
+// output_fd is *not* consumed, timeouts <= 0 will wait forever.
+bool debuggerd_trigger_dump(pid_t pid, android::base::unique_fd output_fd,
+                            enum DebuggerdDumpType dump_type, int timeout_ms);
 
-typedef enum {
-  // dump a crash
-  DEBUGGER_ACTION_CRASH,
-  // dump a tombstone file
-  DEBUGGER_ACTION_DUMP_TOMBSTONE,
-  // dump a backtrace only back to the socket
-  DEBUGGER_ACTION_DUMP_BACKTRACE,
-} debugger_action_t;
-
-// Make sure that all values have a fixed size so that this structure
-// is the same for 32 bit and 64 bit processes.
-typedef struct __attribute__((packed)) {
-  int32_t action;
-  pid_t tid;
-  pid_t ignore_tid;
-  uint64_t abort_msg_address;
-} debugger_msg_t;
-
-// These callbacks are called in a signal handler, and thus must be async signal safe.
-// If null, the callbacks will not be called.
-typedef struct {
-  struct abort_msg_t* (*get_abort_message)();
-  void (*post_dump)();
-} debuggerd_callbacks_t;
-
-void debuggerd_init(debuggerd_callbacks_t* callbacks);
-
-__END_DECLS
+int dump_backtrace_to_file(pid_t tid, int fd);
+int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
new file mode 100644
index 0000000..7196e0a
--- /dev/null
+++ b/debuggerd/include/debuggerd/handler.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <signal.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+// These callbacks are called in a signal handler, and thus must be async signal safe.
+// If null, the callbacks will not be called.
+typedef struct {
+  struct abort_msg_t* (*get_abort_message)();
+  void (*post_dump)();
+} debuggerd_callbacks_t;
+
+void debuggerd_init(debuggerd_callbacks_t* callbacks);
+
+// DEBUGGER_ACTION_DUMP_TOMBSTONE and DEBUGGER_ACTION_DUMP_BACKTRACE are both
+// triggered via DEBUGGER_SIGNAL. The debugger_action_t is sent via si_value
+// using sigqueue(2) or equivalent. If no si_value is specified (e.g. if the
+// signal is sent by kill(2)), the default behavior is to print the backtrace
+// to the log.
+#define DEBUGGER_SIGNAL (__SIGRTMIN + 3)
+
+static void __attribute__((__unused__)) debuggerd_register_handlers(struct sigaction* action) {
+  sigaction(SIGABRT, action, nullptr);
+  sigaction(SIGBUS, action, nullptr);
+  sigaction(SIGFPE, action, nullptr);
+  sigaction(SIGILL, action, nullptr);
+  sigaction(SIGSEGV, action, nullptr);
+#if defined(SIGSTKFLT)
+  sigaction(SIGSTKFLT, action, nullptr);
+#endif
+  sigaction(SIGSYS, action, nullptr);
+  sigaction(SIGTRAP, action, nullptr);
+  sigaction(DEBUGGER_SIGNAL, action, nullptr);
+}
+
+__END_DECLS
diff --git a/debuggerd/include/debuggerd/protocol.h b/debuggerd/include/debuggerd/protocol.h
new file mode 100644
index 0000000..bb2ab0d
--- /dev/null
+++ b/debuggerd/include/debuggerd/protocol.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <stdint.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 kTombstonedInterceptSocketName[] = "tombstoned_intercept";
+
+enum class CrashPacketType : uint8_t {
+  // Initial request from crash_dump.
+  kDumpRequest = 0,
+
+  // Notification of a completed crash dump.
+  // Sent after a dump is completed and the process has been untraced, but
+  // before it has been resumed with SIGCONT.
+  kCompletedDump,
+
+  // Responses to kRequest.
+  // kPerformDump sends along an output fd via cmsg(3).
+  kPerformDump = 128,
+  kAbortDump,
+};
+
+struct DumpRequest {
+  int32_t pid;
+};
+
+// The full packet must always be written, regardless of whether the union is used.
+struct TombstonedCrashPacket {
+  CrashPacketType packet_type;
+  union {
+    DumpRequest dump_request;
+  } packet;
+};
+
+// Comes with a file descriptor via SCM_RIGHTS.
+// This packet should be sent before an actual dump happens.
+struct InterceptRequest {
+  int32_t pid;
+};
+
+// Sent either immediately upon failure, or when the intercept has been used.
+struct InterceptResponse {
+  uint8_t success;          // 0 or 1
+  char error_message[127];  // always null-terminated
+};
diff --git a/debuggerd/include/debuggerd/util.h b/debuggerd/include/debuggerd/util.h
new file mode 100644
index 0000000..6051714
--- /dev/null
+++ b/debuggerd/include/debuggerd/util.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <android-base/unique_fd.h>
+
+// *** WARNING ***
+// tombstoned's sockets are SOCK_SEQPACKET sockets.
+// Short reads are treated as errors and short writes are assumed to not happen.
+
+// Sends a packet with an attached fd.
+ssize_t send_fd(int sockfd, const void* _Nonnull data, size_t len, android::base::unique_fd fd);
+
+// Receives a packet and optionally, its attached fd.
+// If out_fd is non-null, packets can optionally have an attached fd.
+// If out_fd is null, received packets must not have an attached fd.
+//
+// Errors:
+//   EOVERFLOW: sockfd is SOCK_DGRAM or SOCK_SEQPACKET and buffer is too small.
+//              The first len bytes of the packet are stored in data, but the
+//              rest of the packet is dropped.
+//   ERANGE:    too many file descriptors were attached to the packet.
+//   ENOMSG:    not enough file descriptors were attached to the packet.
+//
+//   plus any errors returned by the underlying recvmsg.
+ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
+                android::base::unique_fd* _Nullable out_fd);
+
+bool Pipe(android::base::unique_fd* read, android::base::unique_fd* write);
diff --git a/debuggerd/arm/machine.cpp b/debuggerd/libdebuggerd/arm/machine.cpp
similarity index 98%
rename from debuggerd/arm/machine.cpp
rename to debuggerd/libdebuggerd/arm/machine.cpp
index 292edcb..78c2306 100644
--- a/debuggerd/arm/machine.cpp
+++ b/debuggerd/libdebuggerd/arm/machine.cpp
@@ -22,8 +22,8 @@
 #include <string.h>
 #include <sys/ptrace.h>
 
-#include <android/log.h>
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
diff --git a/debuggerd/arm64/machine.cpp b/debuggerd/libdebuggerd/arm64/machine.cpp
similarity index 98%
rename from debuggerd/arm64/machine.cpp
rename to debuggerd/libdebuggerd/arm64/machine.cpp
index cd1bd52..e7bf79a 100644
--- a/debuggerd/arm64/machine.cpp
+++ b/debuggerd/libdebuggerd/arm64/machine.cpp
@@ -24,8 +24,8 @@
 #include <sys/ptrace.h>
 #include <sys/uio.h>
 
-#include <android/log.h>
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
diff --git a/debuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
similarity index 98%
rename from debuggerd/backtrace.cpp
rename to debuggerd/libdebuggerd/backtrace.cpp
index 06c1efe..0664442 100644
--- a/debuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -31,8 +31,8 @@
 #include <memory>
 #include <string>
 
-#include <android/log.h>
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "backtrace.h"
 
diff --git a/debuggerd/elf_utils.cpp b/debuggerd/libdebuggerd/elf_utils.cpp
similarity index 99%
rename from debuggerd/elf_utils.cpp
rename to debuggerd/libdebuggerd/elf_utils.cpp
index d760a37..4e798e2 100644
--- a/debuggerd/elf_utils.cpp
+++ b/debuggerd/libdebuggerd/elf_utils.cpp
@@ -23,9 +23,9 @@
 
 #include <string>
 
-#include <android/log.h>
 #include <android-base/stringprintf.h>
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "elf_utils.h"
 
diff --git a/debuggerd/backtrace.h b/debuggerd/libdebuggerd/include/backtrace.h
similarity index 100%
rename from debuggerd/backtrace.h
rename to debuggerd/libdebuggerd/include/backtrace.h
diff --git a/debuggerd/elf_utils.h b/debuggerd/libdebuggerd/include/elf_utils.h
similarity index 100%
rename from debuggerd/elf_utils.h
rename to debuggerd/libdebuggerd/include/elf_utils.h
diff --git a/debuggerd/machine.h b/debuggerd/libdebuggerd/include/machine.h
similarity index 100%
rename from debuggerd/machine.h
rename to debuggerd/libdebuggerd/include/machine.h
diff --git a/debuggerd/open_files_list.h b/debuggerd/libdebuggerd/include/open_files_list.h
similarity index 100%
rename from debuggerd/open_files_list.h
rename to debuggerd/libdebuggerd/include/open_files_list.h
diff --git a/debuggerd/tombstone.h b/debuggerd/libdebuggerd/include/tombstone.h
similarity index 79%
rename from debuggerd/tombstone.h
rename to debuggerd/libdebuggerd/include/tombstone.h
index 126f804..aed71de 100644
--- a/debuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/tombstone.h
@@ -23,6 +23,8 @@
 #include <set>
 #include <string>
 
+#include "open_files_list.h"
+
 class BacktraceMap;
 
 /* Create and open a tombstone file for writing.
@@ -33,8 +35,11 @@
 
 /* Creates a tombstone file and writes the crash dump to it. */
 void engrave_tombstone(int tombstone_fd, BacktraceMap* map,
-                       const OpenFilesList& open_files, pid_t pid, pid_t tid,
-                       const std::set<pid_t>& siblings, uintptr_t abort_msg_address,
+                       const OpenFilesList* open_files, pid_t pid, pid_t tid,
+                       const std::set<pid_t>* siblings, uintptr_t abort_msg_address,
                        std::string* amfd_data);
 
+void engrave_tombstone_ucontext(int tombstone_fd, pid_t pid, pid_t tid, uintptr_t abort_msg_address,
+                                siginfo_t* siginfo, ucontext_t* ucontext);
+
 #endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/utility.h b/debuggerd/libdebuggerd/include/utility.h
similarity index 96%
rename from debuggerd/utility.h
rename to debuggerd/libdebuggerd/include/utility.h
index f7a3f73..bbc4546 100644
--- a/debuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/utility.h
@@ -18,6 +18,7 @@
 #ifndef _DEBUGGERD_UTILITY_H
 #define _DEBUGGERD_UTILITY_H
 
+#include <signal.h>
 #include <stdbool.h>
 #include <sys/types.h>
 
@@ -78,7 +79,7 @@
 void _LOG(log_t* log, logtype ltype, const char *fmt, ...)
         __attribute__ ((format(printf, 3, 4)));
 
-int wait_for_signal(pid_t tid);
+bool wait_for_signal(pid_t tid, siginfo_t* siginfo);
 
 void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...);
 
diff --git a/debuggerd/mips/machine.cpp b/debuggerd/libdebuggerd/mips/machine.cpp
similarity index 99%
rename from debuggerd/mips/machine.cpp
rename to debuggerd/libdebuggerd/mips/machine.cpp
index 99a9d65..cbf272a 100644
--- a/debuggerd/mips/machine.cpp
+++ b/debuggerd/libdebuggerd/mips/machine.cpp
@@ -22,8 +22,8 @@
 #include <string.h>
 #include <sys/ptrace.h>
 
-#include <android/log.h>
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
diff --git a/debuggerd/mips64/machine.cpp b/debuggerd/libdebuggerd/mips64/machine.cpp
similarity index 99%
rename from debuggerd/mips64/machine.cpp
rename to debuggerd/libdebuggerd/mips64/machine.cpp
index ecd1ca2..0a8d532 100644
--- a/debuggerd/mips64/machine.cpp
+++ b/debuggerd/libdebuggerd/mips64/machine.cpp
@@ -22,8 +22,8 @@
 #include <string.h>
 #include <sys/ptrace.h>
 
-#include <android/log.h>
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
diff --git a/debuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp
similarity index 98%
rename from debuggerd/open_files_list.cpp
rename to debuggerd/libdebuggerd/open_files_list.cpp
index 5ef2abc..5c7ea70 100644
--- a/debuggerd/open_files_list.cpp
+++ b/debuggerd/libdebuggerd/open_files_list.cpp
@@ -29,7 +29,7 @@
 #include <vector>
 
 #include <android-base/file.h>
-#include <android/log.h>
+#include <log/log.h>
 
 #include "open_files_list.h"
 
diff --git a/debuggerd/test/BacktraceMock.h b/debuggerd/libdebuggerd/test/BacktraceMock.h
similarity index 100%
rename from debuggerd/test/BacktraceMock.h
rename to debuggerd/libdebuggerd/test/BacktraceMock.h
diff --git a/debuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
similarity index 100%
rename from debuggerd/test/dump_memory_test.cpp
rename to debuggerd/libdebuggerd/test/dump_memory_test.cpp
diff --git a/debuggerd/test/elf_fake.cpp b/debuggerd/libdebuggerd/test/elf_fake.cpp
similarity index 100%
rename from debuggerd/test/elf_fake.cpp
rename to debuggerd/libdebuggerd/test/elf_fake.cpp
diff --git a/debuggerd/test/elf_fake.h b/debuggerd/libdebuggerd/test/elf_fake.h
similarity index 100%
rename from debuggerd/test/elf_fake.h
rename to debuggerd/libdebuggerd/test/elf_fake.h
diff --git a/debuggerd/test/host_signal_fixup.h b/debuggerd/libdebuggerd/test/host_signal_fixup.h
similarity index 100%
rename from debuggerd/test/host_signal_fixup.h
rename to debuggerd/libdebuggerd/test/host_signal_fixup.h
diff --git a/debuggerd/test/log_fake.cpp b/debuggerd/libdebuggerd/test/log_fake.cpp
similarity index 100%
rename from debuggerd/test/log_fake.cpp
rename to debuggerd/libdebuggerd/test/log_fake.cpp
diff --git a/debuggerd/test/log_fake.h b/debuggerd/libdebuggerd/test/log_fake.h
similarity index 100%
rename from debuggerd/test/log_fake.h
rename to debuggerd/libdebuggerd/test/log_fake.h
diff --git a/debuggerd/test/open_files_list_test.cpp b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
similarity index 100%
rename from debuggerd/test/open_files_list_test.cpp
rename to debuggerd/libdebuggerd/test/open_files_list_test.cpp
diff --git a/debuggerd/test/property_fake.cpp b/debuggerd/libdebuggerd/test/property_fake.cpp
similarity index 100%
rename from debuggerd/test/property_fake.cpp
rename to debuggerd/libdebuggerd/test/property_fake.cpp
diff --git a/debuggerd/test/ptrace_fake.cpp b/debuggerd/libdebuggerd/test/ptrace_fake.cpp
similarity index 100%
rename from debuggerd/test/ptrace_fake.cpp
rename to debuggerd/libdebuggerd/test/ptrace_fake.cpp
diff --git a/debuggerd/test/ptrace_fake.h b/debuggerd/libdebuggerd/test/ptrace_fake.h
similarity index 100%
rename from debuggerd/test/ptrace_fake.h
rename to debuggerd/libdebuggerd/test/ptrace_fake.h
diff --git a/debuggerd/test/sys/system_properties.h b/debuggerd/libdebuggerd/test/sys/system_properties.h
similarity index 98%
rename from debuggerd/test/sys/system_properties.h
rename to debuggerd/libdebuggerd/test/sys/system_properties.h
index 9d44345..1f4f58a 100644
--- a/debuggerd/test/sys/system_properties.h
+++ b/debuggerd/libdebuggerd/test/sys/system_properties.h
@@ -32,7 +32,6 @@
 // This is just enough to get the property code to compile on
 // the host.
 
-#define PROP_NAME_MAX   32
 #define PROP_VALUE_MAX  92
 
 #endif // _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
diff --git a/debuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
similarity index 100%
rename from debuggerd/test/tombstone_test.cpp
rename to debuggerd/libdebuggerd/test/tombstone_test.cpp
diff --git a/debuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
similarity index 86%
rename from debuggerd/tombstone.cpp
rename to debuggerd/libdebuggerd/tombstone.cpp
index e76edb9..4686bfd 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -32,6 +32,7 @@
 #include <memory>
 #include <string>
 
+#include <android/log.h>
 #include <android-base/stringprintf.h>
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
@@ -39,9 +40,8 @@
 #include <log/log.h>
 #include <log/logprint.h>
 #include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
 
-#include <selinux/android.h>
+#include "debuggerd/handler.h"
 
 #include "backtrace.h"
 #include "elf_utils.h"
@@ -49,6 +49,8 @@
 #include "open_files_list.h"
 #include "tombstone.h"
 
+using android::base::StringPrintf;
+
 #define STACK_WORDS 16
 
 #define MAX_TOMBSTONES  10
@@ -74,7 +76,7 @@
 }
 
 static const char* get_signame(int sig) {
-  switch(sig) {
+  switch (sig) {
     case SIGABRT: return "SIGABRT";
     case SIGBUS: return "SIGBUS";
     case SIGFPE: return "SIGFPE";
@@ -86,6 +88,7 @@
     case SIGSTOP: return "SIGSTOP";
     case SIGSYS: return "SIGSYS";
     case SIGTRAP: return "SIGTRAP";
+    case DEBUGGER_SIGNAL: return "<debuggerd signal>";
     default: return "?";
   }
 }
@@ -194,14 +197,32 @@
   _LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
 }
 
-static void dump_signal_info(log_t* log, pid_t tid) {
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) {
-    ALOGE("cannot get siginfo: %s\n", strerror(errno));
-    return;
+static void dump_probable_cause(log_t* log, const siginfo_t& si) {
+  std::string cause;
+  if (si.si_signo == SIGSEGV && si.si_code == SEGV_MAPERR) {
+    if (si.si_addr < reinterpret_cast<void*>(4096)) {
+      cause = StringPrintf("null pointer dereference");
+    } else if (si.si_addr == reinterpret_cast<void*>(0xffff0ffc)) {
+      cause = "call to kuser_helper_version";
+    } else if (si.si_addr == reinterpret_cast<void*>(0xffff0fe0)) {
+      cause = "call to kuser_get_tls";
+    } else if (si.si_addr == reinterpret_cast<void*>(0xffff0fc0)) {
+      cause = "call to kuser_cmpxchg";
+    } else if (si.si_addr == reinterpret_cast<void*>(0xffff0fa0)) {
+      cause = "call to kuser_memory_barrier";
+    } else if (si.si_addr == reinterpret_cast<void*>(0xffff0f60)) {
+      cause = "call to kuser_cmpxchg64";
+    }
+  } else if (si.si_signo == SIGSYS && si.si_code == SYS_SECCOMP) {
+    cause = StringPrintf("seccomp prevented call to disallowed %s system call %d",
+                         ABI_STRING, si.si_syscall);
   }
 
+  if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str());
+}
+
+static void dump_signal_info(log_t* log, const siginfo_t* siginfo) {
+  const siginfo_t& si = *siginfo;
   char addr_desc[32]; // ", fault addr 0x1234"
   if (signal_has_si_addr(si.si_signo, si.si_code)) {
     snprintf(addr_desc, sizeof(addr_desc), "%p", si.si_addr);
@@ -211,6 +232,19 @@
 
   _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s), fault addr %s\n", si.si_signo,
        get_signame(si.si_signo), si.si_code, get_sigcode(si.si_signo, si.si_code), addr_desc);
+
+  dump_probable_cause(log, si);
+}
+
+static void dump_signal_info(log_t* log, pid_t tid) {
+  siginfo_t si;
+  memset(&si, 0, sizeof(si));
+  if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) {
+    ALOGE("cannot get siginfo: %s\n", strerror(errno));
+    return;
+  }
+
+  dump_signal_info(log, &si);
 }
 
 static void dump_thread_info(log_t* log, pid_t pid, pid_t tid) {
@@ -261,11 +295,11 @@
     line = "    ";
     if (i == 0 && label >= 0) {
       // Print the label once.
-      line += android::base::StringPrintf("#%02d  ", label);
+      line += StringPrintf("#%02d  ", label);
     } else {
       line += "     ";
     }
-    line += android::base::StringPrintf("%" PRIPTR "  %" PRIPTR, *sp, stack_data[i]);
+    line += StringPrintf("%" PRIPTR "  %" PRIPTR, *sp, stack_data[i]);
 
     backtrace_map_t map;
     backtrace->FillInMap(stack_data[i], &map);
@@ -276,7 +310,7 @@
       if (!func_name.empty()) {
         line += " (" + func_name;
         if (offset) {
-          line += android::base::StringPrintf("+%" PRIuPTR, offset);
+          line += StringPrintf("+%" PRIuPTR, offset);
         }
         line += ')';
       }
@@ -335,11 +369,11 @@
 static std::string get_addr_string(uintptr_t addr) {
   std::string addr_str;
 #if defined(__LP64__)
-  addr_str = android::base::StringPrintf("%08x'%08x",
-                                         static_cast<uint32_t>(addr >> 32),
-                                         static_cast<uint32_t>(addr & 0xffffffff));
+  addr_str = StringPrintf("%08x'%08x",
+                          static_cast<uint32_t>(addr >> 32),
+                          static_cast<uint32_t>(addr & 0xffffffff));
 #else
-  addr_str = android::base::StringPrintf("%08x", addr);
+  addr_str = StringPrintf("%08x", addr);
 #endif
   return addr_str;
 }
@@ -425,8 +459,7 @@
     } else {
       line += '-';
     }
-    line += android::base::StringPrintf("  %8" PRIxPTR "  %8" PRIxPTR,
-                                        it->offset, it->end - it->start);
+    line += StringPrintf("  %8" PRIxPTR "  %8" PRIxPTR, it->offset, it->end - it->start);
     bool space_needed = true;
     if (it->name.length() > 0) {
       space_needed = false;
@@ -440,7 +473,7 @@
       if (space_needed) {
         line += ' ';
       }
-      line += android::base::StringPrintf(" (load base 0x%" PRIxPTR ")", it->load_base);
+      line += StringPrintf(" (load base 0x%" PRIxPTR ")", it->load_base);
     }
     _LOG(log, logtype::MAPS, "%s\n", line.c_str());
   }
@@ -622,10 +655,12 @@
 
 // Dumps all information about the specified pid to the tombstone.
 static void dump_crash(log_t* log, BacktraceMap* map,
-                       const OpenFilesList& open_files, pid_t pid, pid_t tid,
-                       const std::set<pid_t>& siblings, uintptr_t abort_msg_address) {
+                       const OpenFilesList* open_files, pid_t pid, pid_t tid,
+                       const std::set<pid_t>* siblings, uintptr_t abort_msg_address) {
   // don't copy log messages to tombstone unless this is a dev device
-  bool want_logs = __android_log_is_debuggable();
+  char value[PROPERTY_VALUE_MAX];
+  property_get("ro.debuggable", value, "0");
+  bool want_logs = (value[0] == '1');
 
   _LOG(log, logtype::HEADER,
        "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
@@ -635,14 +670,16 @@
     dump_logs(log, pid, 5);
   }
 
-  if (!siblings.empty()) {
-    for (pid_t sibling : siblings) {
+  if (siblings && !siblings->empty()) {
+    for (pid_t sibling : *siblings) {
       dump_thread(log, pid, sibling, map, 0, false);
     }
   }
 
-  _LOG(log, logtype::OPEN_FILES, "\nopen files:\n");
-  dump_open_files_list_to_log(open_files, log, "    ");
+  if (open_files) {
+    _LOG(log, logtype::OPEN_FILES, "\nopen files:\n");
+    dump_open_files_list_to_log(*open_files, log, "    ");
+  }
 
   if (want_logs) {
     dump_logs(log, pid, 0);
@@ -703,19 +740,34 @@
 }
 
 void engrave_tombstone(int tombstone_fd, BacktraceMap* map,
-                       const OpenFilesList& open_files, pid_t pid, pid_t tid,
-                       const std::set<pid_t>& siblings, uintptr_t abort_msg_address,
+                       const OpenFilesList* open_files, pid_t pid, pid_t tid,
+                       const std::set<pid_t>* siblings, uintptr_t abort_msg_address,
                        std::string* amfd_data) {
   log_t log;
   log.current_tid = tid;
   log.crashed_tid = tid;
-
-  if (tombstone_fd < 0) {
-    ALOGE("debuggerd: skipping tombstone write, nothing to do.\n");
-    return;
-  }
-
   log.tfd = tombstone_fd;
   log.amfd_data = amfd_data;
   dump_crash(&log, map, open_files, pid, tid, siblings, abort_msg_address);
 }
+
+void engrave_tombstone_ucontext(int tombstone_fd, pid_t pid, pid_t tid, uintptr_t abort_msg_address,
+                                siginfo_t* siginfo, ucontext_t* ucontext) {
+  log_t log;
+  log.current_tid = tid;
+  log.crashed_tid = tid;
+  log.tfd = tombstone_fd;
+  log.amfd_data = nullptr;
+
+  dump_thread_info(&log, pid, tid);
+  dump_signal_info(&log, siginfo);
+
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid));
+  dump_abort_message(backtrace.get(), &log, abort_msg_address);
+  // TODO: Dump registers from the ucontext.
+  if (backtrace->Unwind(0, ucontext)) {
+    dump_backtrace_and_stack(backtrace.get(), &log);
+  } else {
+    ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
+  }
+}
diff --git a/debuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
similarity index 95%
rename from debuggerd/utility.cpp
rename to debuggerd/libdebuggerd/utility.cpp
index 419d36c..57209aa 100644
--- a/debuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -71,21 +71,25 @@
   }
 }
 
-int wait_for_signal(pid_t tid) {
+bool wait_for_signal(pid_t tid, siginfo_t* siginfo) {
   while (true) {
     int status;
     pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL));
     if (n == -1) {
       ALOGE("waitpid failed: tid %d, %s", tid, strerror(errno));
-      return -1;
+      return false;
     } else if (n == tid) {
       if (WIFSTOPPED(status)) {
-        return WSTOPSIG(status);
+        if (ptrace(PTRACE_GETSIGINFO, tid, nullptr, siginfo) != 0) {
+          ALOGE("PTRACE_GETSIGINFO failed: %s", strerror(errno));
+          return false;
+        }
+        return true;
       } else {
         ALOGE("unexpected waitpid response: n=%d, status=%08x\n", n, status);
         // This is the only circumstance under which we can allow a detach
         // to fail with ESRCH, which indicates the tid has exited.
-        return -1;
+        return false;
       }
     }
   }
diff --git a/debuggerd/x86/machine.cpp b/debuggerd/libdebuggerd/x86/machine.cpp
similarity index 98%
rename from debuggerd/x86/machine.cpp
rename to debuggerd/libdebuggerd/x86/machine.cpp
index a6f21e1..af10817 100644
--- a/debuggerd/x86/machine.cpp
+++ b/debuggerd/libdebuggerd/x86/machine.cpp
@@ -21,8 +21,8 @@
 #include <string.h>
 #include <sys/ptrace.h>
 
-#include <android/log.h>
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
diff --git a/debuggerd/x86_64/machine.cpp b/debuggerd/libdebuggerd/x86_64/machine.cpp
similarity index 98%
rename from debuggerd/x86_64/machine.cpp
rename to debuggerd/libdebuggerd/x86_64/machine.cpp
index 705e12d..bf2c2b4 100644
--- a/debuggerd/x86_64/machine.cpp
+++ b/debuggerd/libdebuggerd/x86_64/machine.cpp
@@ -18,12 +18,12 @@
 
 #include <errno.h>
 #include <stdint.h>
-#include <sys/ptrace.h>
 #include <string.h>
+#include <sys/ptrace.h>
 #include <sys/user.h>
 
-#include <android/log.h>
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
diff --git a/debuggerd/signal_sender.cpp b/debuggerd/signal_sender.cpp
index 7fe4dee..42a8e77 100644
--- a/debuggerd/signal_sender.cpp
+++ b/debuggerd/signal_sender.cpp
@@ -27,7 +27,7 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include <android/log.h>
+#include <log/log.h>
 
 #include "signal_sender.h"
 
diff --git a/debuggerd/signal_sender.h b/debuggerd/signal_sender.h
deleted file mode 100644
index 0443272..0000000
--- a/debuggerd/signal_sender.h
+++ /dev/null
@@ -1,30 +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 _DEBUGGERD_SIGNAL_SENDER_H
-#define _DEBUGGERD_SIGNAL_SENDER_H
-
-#include <sys/types.h>
-
-bool start_signal_sender();
-bool stop_signal_sender();
-
-// Sends a signal to a target process or thread.
-// If tid is greater than zero, this performs tgkill(pid, tid, signal).
-// Otherwise, it performs kill(pid, signal).
-bool send_signal(pid_t pid, pid_t tid, int signal);
-
-#endif
diff --git a/debuggerd/test/selinux/android.h b/debuggerd/test/selinux/android.h
deleted file mode 100644
index abed087..0000000
--- a/debuggerd/test/selinux/android.h
+++ /dev/null
@@ -1,17 +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.
- */
-
-extern "C" int selinux_android_restorecon(const char*, unsigned int);
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
new file mode 100644
index 0000000..789260d
--- /dev/null
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright 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 "intercept_manager.h"
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include <unordered_map>
+
+#include <event2/event.h>
+#include <event2/listener.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+#include "debuggerd/protocol.h"
+#include "debuggerd/util.h"
+
+using android::base::unique_fd;
+
+static void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {
+  auto intercept = reinterpret_cast<Intercept*>(arg);
+  InterceptManager* intercept_manager = intercept->intercept_manager;
+
+  CHECK_EQ(sockfd, intercept->sockfd.get());
+
+  // If we can read, either we received unexpected data from the other side, or the other side
+  // closed their end of the socket. Either way, kill the intercept.
+
+  // Ownership of intercept differs based on whether we've registered it with InterceptManager.
+  if (!intercept->registered) {
+    delete intercept;
+  } else {
+    auto it = intercept_manager->intercepts.find(intercept->intercept_pid);
+    if (it == intercept_manager->intercepts.end()) {
+      LOG(FATAL) << "intercept close callback called after intercept was already removed?";
+    }
+    if (it->second.get() != intercept) {
+      LOG(FATAL) << "intercept close callback has different Intercept from InterceptManager?";
+    }
+
+    const char* reason;
+    if ((event & EV_TIMEOUT) != 0) {
+      reason = "due to timeout";
+    } else {
+      reason = "due to input";
+    }
+
+    LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " terminated " << reason;
+    intercept_manager->intercepts.erase(it);
+  }
+}
+
+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;
+
+  CHECK_EQ(sockfd, intercept->sockfd.get());
+
+  if ((ev & EV_TIMEOUT) != 0) {
+    LOG(WARNING) << "tombstoned didn't receive InterceptRequest before timeout";
+    goto fail;
+  } else if ((ev & EV_READ) == 0) {
+    LOG(WARNING) << "tombstoned received unexpected event on intercept socket";
+    goto fail;
+  }
+
+  {
+    unique_fd rcv_fd;
+    InterceptRequest intercept_request;
+    ssize_t result = recv_fd(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
+
+    if (result == -1) {
+      PLOG(WARNING) << "failed to read from intercept socket";
+      goto fail;
+    } else if (result != sizeof(intercept_request)) {
+      LOG(WARNING) << "intercept socket received short read of length " << result << " (expected "
+                   << sizeof(intercept_request) << ")";
+      goto fail;
+    }
+
+    // Move the received FD to the upper half, in order to more easily notice FD leaks.
+    int moved_fd = fcntl(rcv_fd.get(), F_DUPFD, 512);
+    if (moved_fd == -1) {
+      LOG(WARNING) << "failed to move received fd (" << rcv_fd.get() << ")";
+      goto fail;
+    }
+    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()) {
+      InterceptResponse response = {};
+      snprintf(response.error_message, sizeof(response.error_message), "invalid pid %" PRId32,
+               intercept_request.pid);
+      TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
+      goto fail;
+    }
+
+    intercept->intercept_pid = intercept_request.pid;
+
+    // Register the intercept with the InterceptManager.
+    if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
+      InterceptResponse response = {};
+      snprintf(response.error_message, sizeof(response.error_message),
+               "pid %" PRId32 " already intercepted", intercept_request.pid);
+      TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
+      LOG(WARNING) << response.error_message;
+      goto fail;
+    }
+
+    intercept->output_fd = std::move(rcv_fd);
+    intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
+    intercept->registered = true;
+
+    LOG(INFO) << "tombstoned registered intercept for pid " << intercept_request.pid;
+
+    // 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).
+    event_assign(intercept->intercept_event, intercept_manager->base, sockfd, EV_READ | EV_TIMEOUT,
+                 intercept_close_cb, arg);
+
+    struct timeval timeout = { .tv_sec = 10, .tv_usec = 0 };
+    event_add(intercept->intercept_event, &timeout);
+  }
+
+  return;
+
+fail:
+  delete intercept;
+}
+
+static void intercept_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
+                                void* arg) {
+  Intercept* intercept = new Intercept();
+  intercept->intercept_manager = static_cast<InterceptManager*>(arg);
+  intercept->sockfd.reset(sockfd);
+
+  struct timeval timeout = { 1, 0 };
+  event_base* base = evconnlistener_get_base(listener);
+  event* intercept_event =
+    event_new(base, sockfd, EV_TIMEOUT | EV_READ, intercept_request_cb, intercept);
+  intercept->intercept_event = intercept_event;
+  event_add(intercept_event, &timeout);
+}
+
+InterceptManager::InterceptManager(event_base* base, int intercept_socket) : base(base) {
+  this->listener = evconnlistener_new(base, intercept_accept_cb, this, -1, LEV_OPT_CLOSE_ON_FREE,
+                                      intercept_socket);
+}
+
+bool InterceptManager::GetIntercept(pid_t pid, android::base::unique_fd* out_fd) {
+  auto it = this->intercepts.find(pid);
+  if (it == this->intercepts.end()) {
+    return false;
+  }
+
+  auto intercept = std::move(it->second);
+  this->intercepts.erase(it);
+
+  LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid;
+  InterceptResponse response = {};
+  response.success = 1;
+  TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
+  *out_fd = std::move(intercept->output_fd);
+
+  return true;
+}
diff --git a/debuggerd/tombstoned/intercept_manager.h b/debuggerd/tombstoned/intercept_manager.h
new file mode 100644
index 0000000..cb5db62
--- /dev/null
+++ b/debuggerd/tombstoned/intercept_manager.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <unordered_map>
+
+#include <event2/event.h>
+#include <event2/listener.h>
+
+#include <android-base/unique_fd.h>
+
+struct InterceptManager;
+
+struct Intercept {
+  ~Intercept() {
+    event_free(intercept_event);
+  }
+
+  InterceptManager* intercept_manager = nullptr;
+  event* intercept_event = nullptr;
+  android::base::unique_fd sockfd;
+
+  pid_t intercept_pid = -1;
+  android::base::unique_fd output_fd;
+  bool registered = false;
+};
+
+struct InterceptManager {
+  event_base* base;
+  std::unordered_map<pid_t, std::unique_ptr<Intercept>> intercepts;
+  evconnlistener* listener = nullptr;
+
+  InterceptManager(event_base* _Nonnull base, int intercept_socket);
+  InterceptManager(InterceptManager& copy) = delete;
+  InterceptManager(InterceptManager&& move) = delete;
+
+  bool GetIntercept(pid_t pid, android::base::unique_fd* out_fd);
+};
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
new file mode 100644
index 0000000..8705ece
--- /dev/null
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -0,0 +1,289 @@
+/*
+ * Copyright 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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <array>
+#include <deque>
+#include <unordered_map>
+
+#include <event2/event.h>
+#include <event2/listener.h>
+#include <event2/thread.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+#include "debuggerd/protocol.h"
+#include "debuggerd/util.h"
+
+#include "intercept_manager.h"
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+static InterceptManager* intercept_manager;
+
+enum CrashStatus {
+  kCrashStatusRunning,
+  kCrashStatusQueued,
+};
+
+// Ownership of Crash is a bit messy.
+// It's either owned by an active event that must have a timeout, or owned by
+// queued_requests, in the case that multiple crashes come in at the same time.
+struct Crash {
+  ~Crash() {
+    event_free(crash_event);
+  }
+
+  unique_fd crash_fd;
+  pid_t crash_pid;
+  event* crash_event = nullptr;
+};
+
+static constexpr char kTombstoneDirectory[] = "/data/tombstones/";
+static constexpr size_t kTombstoneCount = 10;
+static int tombstone_directory_fd = -1;
+static int next_tombstone = 0;
+
+static constexpr size_t kMaxConcurrentDumps = 1;
+static size_t num_concurrent_dumps = 0;
+
+static std::deque<Crash*> queued_requests;
+
+// 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 unique_fd get_tombstone_fd() {
+  // 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;
+  char buf[PATH_MAX];
+  snprintf(buf, sizeof(buf), "tombstone_%02d", next_tombstone);
+  if (unlinkat(tombstone_directory_fd, buf, 0) != 0 && errno != ENOENT) {
+    PLOG(FATAL) << "failed to unlink tombstone at " << kTombstoneDirectory << buf;
+  }
+
+  result.reset(
+    openat(tombstone_directory_fd, buf, O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0700));
+  if (result == -1) {
+    PLOG(FATAL) << "failed to create tombstone at " << kTombstoneDirectory << buf;
+  }
+
+  next_tombstone = (next_tombstone + 1) % kTombstoneCount;
+  return result;
+}
+
+static void dequeue_request(Crash* crash) {
+  ++num_concurrent_dumps;
+
+  unique_fd output_fd;
+  if (!intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
+    output_fd = get_tombstone_fd();
+  }
+
+  TombstonedCrashPacket response = {
+    .packet_type = CrashPacketType::kPerformDump
+  };
+  ssize_t rc = send_fd(crash->crash_fd, &response, sizeof(response), std::move(output_fd));
+  if (rc == -1) {
+    PLOG(WARNING) << "failed to send response to CrashRequest";
+    goto fail;
+  } else if (rc != sizeof(response)) {
+    PLOG(WARNING) << "crash socket write returned short";
+    goto fail;
+  } else {
+    // TODO: Make this configurable by the interceptor?
+    struct timeval timeout = { 10, 0 };
+
+    event_base* base = event_get_base(crash->crash_event);
+    event_assign(crash->crash_event, base, crash->crash_fd, EV_TIMEOUT | EV_READ,
+                 crash_completed_cb, crash);
+    event_add(crash->crash_event, &timeout);
+  }
+  return;
+
+fail:
+  delete 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();
+
+  struct timeval timeout = { 1, 0 };
+  event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash);
+  crash->crash_fd.reset(sockfd);
+  crash->crash_event = crash_event;
+  event_add(crash_event, &timeout);
+}
+
+static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
+  ssize_t rc;
+  Crash* crash = static_cast<Crash*>(arg);
+  TombstonedCrashPacket request = {};
+
+  if ((ev & EV_TIMEOUT) != 0) {
+    LOG(WARNING) << "crash request timed out";
+    goto fail;
+  } else if ((ev & EV_READ) == 0) {
+    LOG(WARNING) << "tombstoned received unexpected event from crash socket";
+    goto fail;
+  }
+
+  rc = TEMP_FAILURE_RETRY(read(sockfd, &request, sizeof(request)));
+  if (rc == -1) {
+    PLOG(WARNING) << "failed to read from crash socket";
+    goto fail;
+  } else if (rc != sizeof(request)) {
+    LOG(WARNING) << "crash socket received short read of length " << rc << " (expected "
+                 << sizeof(request) << ")";
+    goto fail;
+  }
+
+  if (request.packet_type != CrashPacketType::kDumpRequest) {
+    LOG(WARNING) << "unexpected crash packet type, expected kDumpRequest, received  "
+                 << StringPrintf("%#2hhX", request.packet_type);
+    goto fail;
+  }
+
+  crash->crash_pid = request.packet.dump_request.pid;
+  LOG(INFO) << "received crash request for pid " << crash->crash_pid;
+
+  if (num_concurrent_dumps == kMaxConcurrentDumps) {
+    LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
+    queued_requests.push_back(crash);
+  } else {
+    dequeue_request(crash);
+  }
+
+  return;
+
+fail:
+  delete crash;
+}
+
+static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg) {
+  ssize_t rc;
+  Crash* crash = static_cast<Crash*>(arg);
+  TombstonedCrashPacket request = {};
+
+  --num_concurrent_dumps;
+
+  if ((ev & EV_READ) == 0) {
+    goto fail;
+  }
+
+  rc = TEMP_FAILURE_RETRY(read(sockfd, &request, sizeof(request)));
+  if (rc == -1) {
+    PLOG(WARNING) << "failed to read from crash socket";
+    goto fail;
+  } else if (rc != sizeof(request)) {
+    LOG(WARNING) << "crash socket received short read of length " << rc << " (expected "
+                 << sizeof(request) << ")";
+    goto fail;
+  }
+
+  if (request.packet_type != CrashPacketType::kCompletedDump) {
+    LOG(WARNING) << "unexpected crash packet type, expected kCompletedDump, received "
+                 << uint32_t(request.packet_type);
+    goto fail;
+  }
+
+fail:
+  delete crash;
+
+  // If there's something queued up, let them proceed.
+  if (!queued_requests.empty()) {
+    Crash* next_crash = queued_requests.front();
+    queued_requests.pop_front();
+    dequeue_request(next_crash);
+  }
+}
+
+int main(int, char* []) {
+  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);
+
+  if (intercept_socket == -1 || crash_socket == -1) {
+    PLOG(FATAL) << "failed to get socket from init";
+  }
+
+  evutil_make_socket_nonblocking(intercept_socket);
+  evutil_make_socket_nonblocking(crash_socket);
+
+  event_base* base = event_base_new();
+  if (!base) {
+    LOG(FATAL) << "failed to create event_base";
+  }
+
+  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";
+  }
+
+  LOG(INFO) << "tombstoned successfully initialized";
+  event_base_dispatch(base);
+}
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
new file mode 100644
index 0000000..b8345ca
--- /dev/null
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -0,0 +1,10 @@
+service tombstoned /system/bin/tombstoned
+    user tombstoned
+    group system
+
+    # Don't start tombstoned until after the real /data is mounted.
+    class late_start
+
+    socket tombstoned_crash seqpacket 0666 system system
+    socket tombstoned_intercept seqpacket 0666 system system
+    writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
new file mode 100644
index 0000000..738abdf
--- /dev/null
+++ b/debuggerd/util.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 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 "debuggerd/util.h"
+
+#include <sys/socket.h>
+
+#include <utility>
+
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+ssize_t send_fd(int sockfd, const void* data, size_t len, android::base::unique_fd fd) {
+  char cmsg_buf[CMSG_SPACE(sizeof(int))];
+
+  iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
+  msghdr msg = {
+    .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsg_buf, .msg_controllen = sizeof(cmsg_buf),
+  };
+  auto cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+  *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = fd.get();
+
+  return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, 0));
+}
+
+ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
+                android::base::unique_fd* _Nullable out_fd) {
+  char cmsg_buf[CMSG_SPACE(sizeof(int))];
+
+  iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
+  msghdr msg = {
+    .msg_iov = &iov,
+    .msg_iovlen = 1,
+    .msg_control = cmsg_buf,
+    .msg_controllen = sizeof(cmsg_buf),
+    .msg_flags = 0,
+  };
+  auto cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+
+  ssize_t result = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, 0));
+  if (result == -1) {
+    return -1;
+  }
+
+  android::base::unique_fd fd;
+  bool received_fd = msg.msg_controllen == sizeof(cmsg_buf);
+  if (received_fd) {
+    fd.reset(*reinterpret_cast<int*>(CMSG_DATA(cmsg)));
+  }
+
+  if ((msg.msg_flags & MSG_TRUNC) != 0) {
+    errno = EFBIG;
+    return -1;
+  } else if ((msg.msg_flags & MSG_CTRUNC) != 0) {
+    errno = ERANGE;
+    return -1;
+  }
+
+  if (out_fd) {
+    *out_fd = std::move(fd);
+  } else if (received_fd) {
+    errno = ERANGE;
+    return -1;
+  }
+
+  return result;
+}
+
+bool Pipe(android::base::unique_fd* read, android::base::unique_fd* write) {
+  int pipefds[2];
+  if (pipe(pipefds) != 0) {
+    return false;
+  }
+  read->reset(pipefds[0]);
+  write->reset(pipefds[1]);
+  return true;
+}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 286de5b..5610cc0 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -57,8 +57,8 @@
 
 LOCAL_STATIC_LIBRARIES := \
     libziparchive \
-    libext4_utils_host \
-    libsparse_host \
+    libext4_utils \
+    libsparse \
     libutils \
     liblog \
     libz \
diff --git a/fastboot/README.md b/fastboot/README.md
new file mode 100644
index 0000000..022d34b
--- /dev/null
+++ b/fastboot/README.md
@@ -0,0 +1,453 @@
+Fastboot
+--------
+
+The fastboot protocol is a mechanism for communicating with bootloaders
+over USB or ethernet.  It is designed to be very straightforward to implement,
+to allow it to be used across a wide range of devices and from hosts running
+Linux, macOS, or Windows.
+
+
+## Basic Requirements
+
+* USB
+  * Two bulk endpoints (in, out) are required
+  * Max packet size must be 64 bytes for full-speed, 512 bytes for
+    high-speed and 1024 bytes for Super Speed USB.
+  * The protocol is entirely host-driven and synchronous (unlike the
+    multi-channel, bi-directional, asynchronous ADB protocol)
+
+* TCP or UDP
+  * Device must be reachable via IP.
+  * Device will act as the server, fastboot will be the client.
+  * Fastboot data is wrapped in a simple protocol; see below for details.
+
+
+## Transport and Framing
+
+1. Host sends a command, which is an ascii string in a single
+   packet no greater than 64 bytes.
+
+2. Client response with a single packet no greater than 64 bytes.
+   The first four bytes of the response are "OKAY", "FAIL", "DATA",
+   or "INFO".  Additional bytes may contain an (ascii) informative
+   message.
+
+   a. INFO -> the remaining 60 bytes are an informative message
+      (providing progress or diagnostic messages).  They should
+      be displayed and then step #2 repeats
+
+   b. FAIL -> the requested command failed.  The remaining 60 bytes
+      of the response (if present) provide a textual failure message
+      to present to the user.  Stop.
+
+   c. OKAY -> the requested command completed successfully.  Go to #5
+
+   d. DATA -> the requested command is ready for the data phase.
+      A DATA response packet will be 12 bytes long, in the form of
+      DATA00000000 where the 8 digit hexadecimal number represents
+      the total data size to transfer.
+
+3. Data phase.  Depending on the command, the host or client will
+   send the indicated amount of data.  Short packets are always
+   acceptable and zero-length packets are ignored.  This phase continues
+   until the client has sent or received the number of bytes indicated
+   in the "DATA" response above.
+
+4. Client responds with a single packet no greater than 64 bytes.
+   The first four bytes of the response are "OKAY", "FAIL", or "INFO".
+   Similar to #2:
+
+   a. INFO -> display the remaining 60 bytes and return to #4
+
+   b. FAIL -> display the remaining 60 bytes (if present) as a failure
+      reason and consider the command failed.  Stop.
+
+   c. OKAY -> success.  Go to #5
+
+5. Success.  Stop.
+
+
+## Example Session
+
+    Host:    "getvar:version"        request version variable
+
+    Client:  "OKAY0.4"               return version "0.4"
+
+    Host:    "getvar:nonexistant"    request some undefined variable
+
+    Client:  "FAILUnknown variable"  getvar failure; see getvar details below
+
+    Host:    "download:00001234"     request to send 0x1234 bytes of data
+
+    Client:  "DATA00001234"          ready to accept data
+
+    Host:    < 0x1234 bytes >        send data
+
+    Client:  "OKAY"                  success
+
+    Host:    "flash:bootloader"      request to flash the data to the bootloader
+
+    Client:  "INFOerasing flash"     indicate status / progress
+             "INFOwriting flash"
+             "OKAY"                  indicate success
+
+    Host:    "powerdown"             send a command
+
+    Client:  "FAILunknown command"   indicate failure
+
+
+## Command Reference
+
+* Command parameters are indicated by printf-style escape sequences.
+
+* Commands are ascii strings and sent without the quotes (which are
+  for illustration only here) and without a trailing 0 byte.
+
+* Commands that begin with a lowercase letter are reserved for this
+  specification.  OEM-specific commands should not begin with a
+  lowercase letter, to prevent incompatibilities with future specs.
+
+The various currently defined commands are:
+
+    getvar:%s          Read a config/version variable from the bootloader.
+                       The variable contents will be returned after the
+                       OKAY response. If the variable is unknown, the bootloader
+                       should return a FAIL response, optionally with an error
+                       message.
+
+                       Previous versions of this document indicated that getvar
+                       should return an empty OKAY response for unknown
+                       variables, so older devices might exhibit this behavior,
+                       but new implementations should return FAIL instead.
+
+    download:%08x      Write data to memory which will be later used
+                       by "boot", "ramdisk", "flash", etc.  The client
+                       will reply with "DATA%08x" if it has enough
+                       space in RAM or "FAIL" if not.  The size of
+                       the download is remembered.
+
+    verify:%08x        Send a digital signature to verify the downloaded
+                       data.  Required if the bootloader is "secure"
+                       otherwise "flash" and "boot" will be ignored.
+
+    flash:%s           Write the previously downloaded image to the
+                       named partition (if possible).
+
+    erase:%s           Erase the indicated partition (clear to 0xFFs)
+
+    boot               The previously downloaded data is a boot.img
+                       and should be booted according to the normal
+                       procedure for a boot.img
+
+    continue           Continue booting as normal (if possible)
+
+    reboot             Reboot the device.
+
+    reboot-bootloader
+                       Reboot back into the bootloader.
+                       Useful for upgrade processes that require upgrading
+                       the bootloader and then upgrading other partitions
+                       using the new bootloader.
+
+    powerdown          Power off the device.
+
+
+
+## Client Variables
+
+The "getvar:%s" command is used to read client variables which
+represent various information about the device and the software
+on it.
+
+The various currently defined names are:
+
+    version             Version of FastBoot protocol supported.
+                        It should be "0.4" for this document.
+
+    version-bootloader  Version string for the Bootloader.
+
+    version-baseband    Version string of the Baseband Software
+
+    product             Name of the product
+
+    serialno            Product serial number
+
+    secure              If the value is "yes", this is a secure
+                        bootloader requiring a signature before
+                        it will install or boot images.
+
+Names starting with a lowercase character are reserved by this
+specification.  OEM-specific names should not start with lowercase
+characters.
+
+
+## TCP Protocol v1
+
+The TCP protocol is designed to be a simple way to use the fastboot protocol
+over ethernet if USB is not available.
+
+The device will open a TCP server on port 5554 and wait for a fastboot client
+to connect.
+
+### Handshake
+Upon connecting, both sides will send a 4-byte handshake message to ensure they
+are speaking the same protocol. This consists of the ASCII characters "FB"
+followed by a 2-digit base-10 ASCII version number. For example, the version 1
+handshake message will be [FB01].
+
+If either side detects a malformed handshake, it should disconnect.
+
+The protocol version to use must be the minimum of the versions sent by each
+side; if either side cannot speak this protocol version, it should disconnect.
+
+### Fastboot Data
+Once the handshake is complete, fastboot data will be sent as follows:
+
+    [data_size][data]
+
+Where data\_size is an unsigned 8-byte big-endian binary value, and data is the
+fastboot packet. The 8-byte length is intended to provide future-proofing even
+though currently fastboot packets have a 4-byte maximum length.
+
+### Example
+In this example the fastboot host queries the device for two variables,
+"version" and "none".
+
+    Host    <connect to the device on port 5555>
+    Host    FB01
+    Device  FB01
+    Host    [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0E]getvar:version
+    Device  [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x07]OKAY0.4
+    Host    [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0B]getvar:none
+    Device  [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x14]FAILUnknown variable
+    Host    <disconnect>
+
+
+## UDP Protocol v1
+
+The UDP protocol is more complex than TCP since we must implement reliability
+to ensure no packets are lost, but the general concept of wrapping the fastboot
+protocol is the same.
+
+Overview:
+  1. As with TCP, the device will listen on UDP port 5554.
+  2. Maximum UDP packet size is negotiated during initialization.
+  3. The host drives all communication; the device may only send a packet as a
+     response to a host packet.
+  4. If the host does not receive a response in 500ms it will re-transmit.
+
+### UDP Packet format
+
+    +----------+----+-------+-------+--------------------+
+    | Byte #   | 0  |   1   | 2 - 3 |  4+                |
+    +----------+----+-------+-------+--------------------+
+    | Contents | ID | Flags | Seq # | Data               |
+    +----------+----+-------+-------+--------------------+
+
+    ID      Packet ID:
+              0x00: Error.
+              0x01: Query.
+              0x02: Initialization.
+              0x03: Fastboot.
+
+            Packet types are described in more detail below.
+
+    Flags   Packet flags: 0 0 0 0 0 0 0 C
+              C=1 indicates a continuation packet; the data is too large and will
+                  continue in the next packet.
+
+              Remaining bits are reserved for future use and must be set to 0.
+
+    Seq #   2-byte packet sequence number (big-endian). The host will increment
+            this by 1 with each new packet, and the device must provide the
+            corresponding sequence number in the response packets.
+
+    Data    Packet data, not present in all packets.
+
+### Packet Types
+
+    Query
+          The host sends a query packet once on startup to sync with the device.
+          The host will not know the current sequence number, so the device must
+          respond to all query packets regardless of sequence number.
+
+          The response data field should contain a 2-byte big-endian value
+          giving the next expected sequence number.
+
+    Init
+          The host sends an init packet once the query response is returned. The
+          device must abort any in-progress operation and prepare for a new
+          fastboot session. This message is meant to allow recovery if a
+          previous session failed, e.g. due to network error or user Ctrl+C.
+
+          The data field contains two big-endian 2-byte values, a protocol
+          version and the max UDP packet size (including the 4-byte header).
+          Both the host and device will send these values, and in each case
+          the minimum of the sent values must be used.
+
+    Fastboot
+          These packets wrap the fastboot protocol. To write, the host will
+          send a packet with fastboot data, and the device will reply with an
+          empty packet as an ACK. To read, the host will send an empty packet,
+          and the device will reply with fastboot data. The device may not give
+          any data in the ACK packet.
+
+    Error
+          The device may respond to any packet with an error packet to indicate
+          a UDP protocol error. The data field should contain an ASCII string
+          describing the error. This is the only case where a device is allowed
+          to return a packet ID other than the one sent by the host.
+
+### Packet Size
+The maximum packet size is negotiated by the host and device in the Init packet.
+Devices must support at least 512-byte packets, but packet size has a direct
+correlation with download speed, so devices are strongly suggested to support at
+least 1024-byte packets. On a local network with 0.5ms round-trip time this will
+provide transfer rates of ~2MB/s. Over WiFi it will likely be significantly
+less.
+
+Query and Initialization packets, which are sent before size negotiation is
+complete, must always be 512 bytes or less.
+
+### Packet Re-Transmission
+The host will re-transmit any packet that does not receive a response. The
+requirement of exactly one device response packet per host packet is how we
+achieve reliability and in-order delivery of packets.
+
+For simplicity of implementation, there is no windowing of multiple
+unacknowledged packets in this version of the protocol. The host will continue
+to send the same packet until a response is received. Windowing functionality
+may be implemented in future versions if necessary to increase performance.
+
+The first Query packet will only be attempted a small number of times, but
+subsequent packets will attempt to retransmit for at least 1 minute before
+giving up. This means a device may safely ignore host UDP packets for up to 1
+minute during long operations, e.g. writing to flash.
+
+### Continuation Packets
+Any packet may set the continuation flag to indicate that the data is
+incomplete. Large data such as downloading an image may require many
+continuation packets. The receiver should respond to a continuation packet with
+an empty packet to acknowledge receipt. See examples below.
+
+### Summary
+The host starts with a Query packet, then an Initialization packet, after
+which only Fastboot packets are sent. Fastboot packets may contain data from
+the host for writes, or from the device for reads, but not both.
+
+Given a next expected sequence number S and a received packet P, the device
+behavior should be:
+
+    if P is a Query packet:
+      * respond with a Query packet with S in the data field
+    else if P has sequence == S:
+      * process P and take any required action
+      * create a response packet R with the same ID and sequence as P, containing
+        any response data required.
+      * transmit R and save it in case of re-transmission
+      * increment S
+    else if P has sequence == S - 1:
+      * re-transmit the saved response packet R from above
+    else:
+      * ignore the packet
+
+### Examples
+
+In the examples below, S indicates the starting client sequence number.
+
+    Host                                    Client
+    ======================================================================
+    [Initialization, S = 0x55AA]
+    [Host: version 1, 2048-byte packets. Client: version 2, 1024-byte packets.]
+    [Resulting values to use: version = 1, max packet size = 1024]
+    ID   Flag SeqH SeqL Data                ID   Flag SeqH SeqL Data
+    ----------------------------------------------------------------------
+    0x01 0x00 0x00 0x00
+                                            0x01 0x00 0x00 0x00 0x55 0xAA
+    0x02 0x00 0x55 0xAA 0x00 0x01 0x08 0x00
+                                            0x02 0x00 0x55 0xAA 0x00 0x02 0x04 0x00
+
+    ----------------------------------------------------------------------
+    [fastboot "getvar" commands, S = 0x0001]
+    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+    ----------------------------------------------------------------------
+    0x03  0x00  0x00  0x01  getvar:version
+                                            0x03  0x00  0x00  0x01
+    0x03  0x00  0x00  0x02
+                                            0x03  0x00  0x00  0x02  OKAY0.4
+    0x03  0x00  0x00  0x03  getvar:none
+                                            0x03  0x00  0x00  0x03
+    0x03  0x00  0x00  0x04
+                                            0x03  0x00  0x00  0x04  FAILUnknown var
+
+    ----------------------------------------------------------------------
+    [fastboot "INFO" responses, S = 0x0000]
+    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+    ----------------------------------------------------------------------
+    0x03  0x00  0x00  0x00  <command>
+                                            0x03  0x00  0x00  0x00
+    0x03  0x00  0x00  0x01
+                                            0x03  0x00  0x00  0x01  INFOWait1
+    0x03  0x00  0x00  0x02
+                                            0x03  0x00  0x00  0x02  INFOWait2
+    0x03  0x00  0x00  0x03
+                                            0x03  0x00  0x00  0x03  OKAY
+
+    ----------------------------------------------------------------------
+    [Chunking 2100 bytes of data, max packet size = 1024, S = 0xFFFF]
+    ID   Flag SeqH SeqL Data                ID   Flag SeqH SeqL Data
+    ----------------------------------------------------------------------
+    0x03 0x00 0xFF 0xFF download:0000834
+                                            0x03 0x00 0xFF 0xFF
+    0x03 0x00 0x00 0x00
+                                            0x03 0x00 0x00 0x00 DATA0000834
+    0x03 0x01 0x00 0x01 <1020 bytes>
+                                            0x03 0x00 0x00 0x01
+    0x03 0x01 0x00 0x02 <1020 bytes>
+                                            0x03 0x00 0x00 0x02
+    0x03 0x00 0x00 0x03 <60 bytes>
+                                            0x03 0x00 0x00 0x03
+    0x03 0x00 0x00 0x04
+                                            0x03 0x00 0x00 0x04 OKAY
+
+    ----------------------------------------------------------------------
+    [Unknown ID error, S = 0x0000]
+    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+    ----------------------------------------------------------------------
+    0x10  0x00  0x00  0x00
+                                            0x00  0x00  0x00  0x00  <error message>
+
+    ----------------------------------------------------------------------
+    [Host packet loss and retransmission, S = 0x0000]
+    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+    ----------------------------------------------------------------------
+    0x03  0x00  0x00  0x00  getvar:version [lost]
+    0x03  0x00  0x00  0x00  getvar:version [lost]
+    0x03  0x00  0x00  0x00  getvar:version
+                                            0x03  0x00  0x00  0x00
+    0x03  0x00  0x00  0x01
+                                            0x03  0x00  0x00  0x01  OKAY0.4
+
+    ----------------------------------------------------------------------
+    [Client packet loss and retransmission, S = 0x0000]
+    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+    ----------------------------------------------------------------------
+    0x03  0x00  0x00  0x00  getvar:version
+                                            0x03  0x00  0x00  0x00 [lost]
+    0x03  0x00  0x00  0x00  getvar:version
+                                            0x03  0x00  0x00  0x00 [lost]
+    0x03  0x00  0x00  0x00  getvar:version
+                                            0x03  0x00  0x00  0x00
+    0x03  0x00  0x00  0x01
+                                            0x03  0x00  0x00  0x01  OKAY0.4
+
+    ----------------------------------------------------------------------
+    [Host packet delayed, S = 0x0000]
+    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+    ----------------------------------------------------------------------
+    0x03  0x00  0x00  0x00  getvar:version [delayed]
+    0x03  0x00  0x00  0x00  getvar:version
+                                            0x03  0x00  0x00  0x00
+    0x03  0x00  0x00  0x01
+                                            0x03  0x00  0x00  0x01  OKAY0.4
+    0x03  0x00  0x00  0x00  getvar:version [arrives late with old seq#, is ignored]
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 7f4a0dd..a4a0b52 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -358,7 +358,7 @@
             "  devices [-l]                             List all connected devices [with\n"
             "                                           device paths].\n"
             "  continue                                 Continue with autoboot.\n"
-            "  reboot [bootloader]                      Reboot device [into bootloader].\n"
+            "  reboot [bootloader|emergency]            Reboot device [into bootloader or emergency mode].\n"
             "  reboot-bootloader                        Reboot device into bootloader.\n"
             "  help                                     Show this help message.\n"
             "\n"
@@ -1303,6 +1303,36 @@
     return num;
 }
 
+static std::string fb_fix_numeric_var(std::string var) {
+    // Some bootloaders (angler, for example), send spurious leading whitespace.
+    var = android::base::Trim(var);
+    // Some bootloaders (hammerhead, for example) use implicit hex.
+    // This code used to use strtol with base 16.
+    if (!android::base::StartsWith(var, "0x")) var = "0x" + var;
+    return var;
+}
+
+static unsigned fb_get_flash_block_size(Transport* transport, std::string name) {
+    std::string sizeString;
+    if (!fb_getvar(transport, name.c_str(), &sizeString)) {
+        /* This device does not report flash block sizes, so return 0 */
+        return 0;
+    }
+    sizeString = fb_fix_numeric_var(sizeString);
+
+    unsigned size;
+    if (!android::base::ParseUint(sizeString, &size)) {
+        fprintf(stderr, "Couldn't parse %s '%s'.\n", name.c_str(), sizeString.c_str());
+        return 0;
+    }
+    if (size < 4096 || (size & (size - 1)) != 0) {
+        fprintf(stderr, "Invalid %s %u: must be a power of 2 and at least 4096.\n",
+                name.c_str(), size);
+        return 0;
+    }
+    return size;
+}
+
 static void fb_perform_format(Transport* transport,
                               const char* partition, int skip_if_not_supported,
                               const char* type_override, const char* size_override,
@@ -1345,11 +1375,7 @@
         }
         partition_size = size_override;
     }
-    // Some bootloaders (angler, for example), send spurious leading whitespace.
-    partition_size = android::base::Trim(partition_size);
-    // Some bootloaders (hammerhead, for example) use implicit hex.
-    // This code used to use strtol with base 16.
-    if (!android::base::StartsWith(partition_size, "0x")) partition_size = "0x" + partition_size;
+    partition_size = fb_fix_numeric_var(partition_size);
 
     gen = fs_get_generator(partition_type);
     if (!gen) {
@@ -1370,7 +1396,12 @@
     }
 
     fd = fileno(tmpfile());
-    if (fs_generator_generate(gen, fd, size, initial_dir)) {
+
+    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)) {
         fprintf(stderr, "Cannot generate image: %s\n", strerror(errno));
         close(fd);
         return;
@@ -1397,6 +1428,7 @@
     bool wants_wipe = false;
     bool wants_reboot = false;
     bool wants_reboot_bootloader = false;
+    bool wants_reboot_emergency = false;
     bool skip_reboot = false;
     bool wants_set_active = false;
     bool skip_secondary = false;
@@ -1647,6 +1679,10 @@
                     wants_reboot = false;
                     wants_reboot_bootloader = true;
                     skip(1);
+                } else if (!strcmp(*argv, "emergency")) {
+                    wants_reboot = false;
+                    wants_reboot_emergency = true;
+                    skip(1);
                 }
             }
             require(0);
@@ -1742,6 +1778,14 @@
         } else if(!strcmp(*argv, "set_active")) {
             require(2);
             std::string slot = verify_slot(transport, std::string(argv[1]), 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.
+            std::string var;
+            if (!fb_getvar(transport, "slot-count", &var) &&
+                    fb_getvar(transport, "slot-suffixes", &var)) {
+                slot = "_" + slot;
+            }
             fb_set_active(slot.c_str());
             skip(2);
         } else if(!strcmp(*argv, "oem")) {
@@ -1799,6 +1843,9 @@
     } else if (wants_reboot_bootloader) {
         fb_queue_command("reboot-bootloader", "rebooting into bootloader");
         fb_queue_wait_for_disconnect();
+    } else if (wants_reboot_emergency) {
+        fb_queue_command("reboot-emergency", "rebooting into emergency download (EDL) mode");
+        fb_queue_wait_for_disconnect();
     }
 
     return fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/fastboot/fastboot_protocol.txt b/fastboot/fastboot_protocol.txt
deleted file mode 100644
index 2801703..0000000
--- a/fastboot/fastboot_protocol.txt
+++ /dev/null
@@ -1,442 +0,0 @@
-FastBoot  Version  0.4
-----------------------
-
-The fastboot protocol is a mechanism for communicating with bootloaders
-over USB or ethernet.  It is designed to be very straightforward to implement,
-to allow it to be used across a wide range of devices and from hosts running
-Linux, Windows, or OSX.
-
-
-Basic Requirements
-------------------
-
-* USB
-  * Two bulk endpoints (in, out) are required
-  * Max packet size must be 64 bytes for full-speed, 512 bytes for
-    high-speed and 1024 bytes for Super Speed USB.
-  * The protocol is entirely host-driven and synchronous (unlike the
-    multi-channel, bi-directional, asynchronous ADB protocol)
-
-* TCP or UDP
-  * Device must be reachable via IP.
-  * Device will act as the server, fastboot will be the client.
-  * Fastboot data is wrapped in a simple protocol; see below for details.
-
-
-Transport and Framing
----------------------
-
-1. Host sends a command, which is an ascii string in a single
-   packet no greater than 64 bytes.
-
-2. Client response with a single packet no greater than 64 bytes.
-   The first four bytes of the response are "OKAY", "FAIL", "DATA", 
-   or "INFO".  Additional bytes may contain an (ascii) informative
-   message.
-
-   a. INFO -> the remaining 60 bytes are an informative message
-      (providing progress or diagnostic messages).  They should 
-      be displayed and then step #2 repeats
-
-   b. FAIL -> the requested command failed.  The remaining 60 bytes 
-      of the response (if present) provide a textual failure message 
-      to present to the user.  Stop.
-
-   c. OKAY -> the requested command completed successfully.  Go to #5
-
-   d. DATA -> the requested command is ready for the data phase.
-      A DATA response packet will be 12 bytes long, in the form of
-      DATA00000000 where the 8 digit hexadecimal number represents
-      the total data size to transfer.
-
-3. Data phase.  Depending on the command, the host or client will 
-   send the indicated amount of data.  Short packets are always 
-   acceptable and zero-length packets are ignored.  This phase continues
-   until the client has sent or received the number of bytes indicated
-   in the "DATA" response above.
-
-4. Client responds with a single packet no greater than 64 bytes.  
-   The first four bytes of the response are "OKAY", "FAIL", or "INFO".  
-   Similar to #2:
-
-   a. INFO -> display the remaining 60 bytes and return to #4
-   
-   b. FAIL -> display the remaining 60 bytes (if present) as a failure
-      reason and consider the command failed.  Stop.
-
-   c. OKAY -> success.  Go to #5
-
-5. Success.  Stop.
-
-
-Example Session
----------------
-
-Host:    "getvar:version"        request version variable
-
-Client:  "OKAY0.4"               return version "0.4"
-
-Host:    "getvar:nonexistant"    request some undefined variable
-
-Client:  "OKAY"                  return value ""
-
-Host:    "download:00001234"     request to send 0x1234 bytes of data
-
-Client:  "DATA00001234"          ready to accept data
-
-Host:    < 0x1234 bytes >        send data
-
-Client:  "OKAY"                  success
-
-Host:    "flash:bootloader"      request to flash the data to the bootloader
-
-Client:  "INFOerasing flash"     indicate status / progress
-         "INFOwriting flash"
-         "OKAY"                  indicate success
-
-Host:    "powerdown"             send a command
-
-Client:  "FAILunknown command"   indicate failure
-
-
-Command Reference
------------------
-
-* Command parameters are indicated by printf-style escape sequences.
-
-* Commands are ascii strings and sent without the quotes (which are
-  for illustration only here) and without a trailing 0 byte.
-
-* Commands that begin with a lowercase letter are reserved for this
-  specification.  OEM-specific commands should not begin with a 
-  lowercase letter, to prevent incompatibilities with future specs.
-
- "getvar:%s"           Read a config/version variable from the bootloader.
-                       The variable contents will be returned after the
-                       OKAY response.
-
- "download:%08x"       Write data to memory which will be later used
-                       by "boot", "ramdisk", "flash", etc.  The client
-                       will reply with "DATA%08x" if it has enough 
-                       space in RAM or "FAIL" if not.  The size of
-                       the download is remembered.
-
-  "verify:%08x"        Send a digital signature to verify the downloaded
-                       data.  Required if the bootloader is "secure"
-                       otherwise "flash" and "boot" will be ignored.
-
-  "flash:%s"           Write the previously downloaded image to the
-                       named partition (if possible).
-
-  "erase:%s"           Erase the indicated partition (clear to 0xFFs)
-
-  "boot"               The previously downloaded data is a boot.img
-                       and should be booted according to the normal
-                       procedure for a boot.img
-
-  "continue"           Continue booting as normal (if possible)
-
-  "reboot"             Reboot the device.
-
-  "reboot-bootloader"  Reboot back into the bootloader.
-                       Useful for upgrade processes that require upgrading
-                       the bootloader and then upgrading other partitions
-                       using the new bootloader.
-
-  "powerdown"          Power off the device.
-
-
-
-Client Variables
-----------------
-
-The "getvar:%s" command is used to read client variables which
-represent various information about the device and the software
-on it.
-
-The various currently defined names are:
-
-  version             Version of FastBoot protocol supported.
-                      It should be "0.4" for this document.
-
-  version-bootloader  Version string for the Bootloader.
-
-  version-baseband    Version string of the Baseband Software
-
-  product             Name of the product
-
-  serialno            Product serial number
-
-  secure              If the value is "yes", this is a secure
-                      bootloader requiring a signature before
-                      it will install or boot images.
-
-Names starting with a lowercase character are reserved by this
-specification.  OEM-specific names should not start with lowercase
-characters.
-
-
-TCP Protocol v1
----------------
-
-The TCP protocol is designed to be a simple way to use the fastboot protocol
-over ethernet if USB is not available.
-
-The device will open a TCP server on port 5554 and wait for a fastboot client
-to connect.
-
--- Handshake --
-Upon connecting, both sides will send a 4-byte handshake message to ensure they
-are speaking the same protocol. This consists of the ASCII characters "FB"
-followed by a 2-digit base-10 ASCII version number. For example, the version 1
-handshake message will be [FB01].
-
-If either side detects a malformed handshake, it should disconnect.
-
-The protocol version to use must be the minimum of the versions sent by each
-side; if either side cannot speak this protocol version, it should disconnect.
-
--- Fastboot Data --
-Once the handshake is complete, fastboot data will be sent as follows:
-
-  [data_size][data]
-
-Where data_size is an unsigned 8-byte big-endian binary value, and data is the
-fastboot packet. The 8-byte length is intended to provide future-proofing even
-though currently fastboot packets have a 4-byte maximum length.
-
--- Example --
-In this example the fastboot host queries the device for two variables,
-"version" and "none".
-
-Host    <connect to the device on port 5555>
-Host    FB01
-Device  FB01
-Host    [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0E]getvar:version
-Device  [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x07]OKAY0.4
-Host    [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0B]getvar:none
-Device  [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x04]OKAY
-Host    <disconnect>
-
-
-UDP Protocol v1
----------------
-
-The UDP protocol is more complex than TCP since we must implement reliability
-to ensure no packets are lost, but the general concept of wrapping the fastboot
-protocol is the same.
-
-Overview:
-  1. As with TCP, the device will listen on UDP port 5554.
-  2. Maximum UDP packet size is negotiated during initialization.
-  3. The host drives all communication; the device may only send a packet as a
-     response to a host packet.
-  4. If the host does not receive a response in 500ms it will re-transmit.
-
--- UDP Packet format --
-  +----------+----+-------+-------+--------------------+
-  | Byte #   | 0  |   1   | 2 - 3 |  4+                |
-  +----------+----+-------+-------+--------------------+
-  | Contents | ID | Flags | Seq # | Data               |
-  +----------+----+-------+-------+--------------------+
-
-  ID      Packet ID:
-            0x00: Error.
-            0x01: Query.
-            0x02: Initialization.
-            0x03: Fastboot.
-
-          Packet types are described in more detail below.
-
-  Flags   Packet flags: 0 0 0 0 0 0 0 C
-            C=1 indicates a continuation packet; the data is too large and will
-                continue in the next packet.
-
-            Remaining bits are reserved for future use and must be set to 0.
-
-  Seq #   2-byte packet sequence number (big-endian). The host will increment
-          this by 1 with each new packet, and the device must provide the
-          corresponding sequence number in the response packets.
-
-  Data    Packet data, not present in all packets.
-
--- Packet Types --
-Query     The host sends a query packet once on startup to sync with the device.
-          The host will not know the current sequence number, so the device must
-          respond to all query packets regardless of sequence number.
-
-          The response data field should contain a 2-byte big-endian value
-          giving the next expected sequence number.
-
-Init      The host sends an init packet once the query response is returned. The
-          device must abort any in-progress operation and prepare for a new
-          fastboot session. This message is meant to allow recovery if a
-          previous session failed, e.g. due to network error or user Ctrl+C.
-
-          The data field contains two big-endian 2-byte values, a protocol
-          version and the max UDP packet size (including the 4-byte header).
-          Both the host and device will send these values, and in each case
-          the minimum of the sent values must be used.
-
-Fastboot  These packets wrap the fastboot protocol. To write, the host will
-          send a packet with fastboot data, and the device will reply with an
-          empty packet as an ACK. To read, the host will send an empty packet,
-          and the device will reply with fastboot data. The device may not give
-          any data in the ACK packet.
-
-Error     The device may respond to any packet with an error packet to indicate
-          a UDP protocol error. The data field should contain an ASCII string
-          describing the error. This is the only case where a device is allowed
-          to return a packet ID other than the one sent by the host.
-
--- Packet Size --
-The maximum packet size is negotiated by the host and device in the Init packet.
-Devices must support at least 512-byte packets, but packet size has a direct
-correlation with download speed, so devices are strongly suggested to support at
-least 1024-byte packets. On a local network with 0.5ms round-trip time this will
-provide transfer rates of ~2MB/s. Over WiFi it will likely be significantly
-less.
-
-Query and Initialization packets, which are sent before size negotiation is
-complete, must always be 512 bytes or less.
-
--- Packet Re-Transmission --
-The host will re-transmit any packet that does not receive a response. The
-requirement of exactly one device response packet per host packet is how we
-achieve reliability and in-order delivery of packets.
-
-For simplicity of implementation, there is no windowing of multiple
-unacknowledged packets in this version of the protocol. The host will continue
-to send the same packet until a response is received. Windowing functionality
-may be implemented in future versions if necessary to increase performance.
-
-The first Query packet will only be attempted a small number of times, but
-subsequent packets will attempt to retransmit for at least 1 minute before
-giving up. This means a device may safely ignore host UDP packets for up to 1
-minute during long operations, e.g. writing to flash.
-
--- Continuation Packets --
-Any packet may set the continuation flag to indicate that the data is
-incomplete. Large data such as downloading an image may require many
-continuation packets. The receiver should respond to a continuation packet with
-an empty packet to acknowledge receipt. See examples below.
-
--- Summary --
-The host starts with a Query packet, then an Initialization packet, after
-which only Fastboot packets are sent. Fastboot packets may contain data from
-the host for writes, or from the device for reads, but not both.
-
-Given a next expected sequence number S and a received packet P, the device
-behavior should be:
-  if P is a Query packet:
-    * respond with a Query packet with S in the data field
-  else if P has sequence == S:
-    * process P and take any required action
-    * create a response packet R with the same ID and sequence as P, containing
-      any response data required.
-    * transmit R and save it in case of re-transmission
-    * increment S
-  else if P has sequence == S - 1:
-    * re-transmit the saved response packet R from above
-  else:
-    * ignore the packet
-
--- Examples --
-In the examples below, S indicates the starting client sequence number.
-
-Host                                    Client
-======================================================================
-[Initialization, S = 0x55AA]
-[Host: version 1, 2048-byte packets. Client: version 2, 1024-byte packets.]
-[Resulting values to use: version = 1, max packet size = 1024]
-ID   Flag SeqH SeqL Data                ID   Flag SeqH SeqL Data
-----------------------------------------------------------------------
-0x01 0x00 0x00 0x00
-                                        0x01 0x00 0x00 0x00 0x55 0xAA
-0x02 0x00 0x55 0xAA 0x00 0x01 0x08 0x00
-                                        0x02 0x00 0x55 0xAA 0x00 0x02 0x04 0x00
-
-----------------------------------------------------------------------
-[fastboot "getvar" commands, S = 0x0001]
-ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
-----------------------------------------------------------------------
-0x03  0x00  0x00  0x01  getvar:version
-                                        0x03  0x00  0x00  0x01
-0x03  0x00  0x00  0x02
-                                        0x03  0x00  0x00  0x02  OKAY0.4
-0x03  0x00  0x00  0x03  getvar:foo
-                                        0x03  0x00  0x00  0x03
-0x03  0x00  0x00  0x04
-                                        0x03  0x00  0x00  0x04  OKAY
-
-----------------------------------------------------------------------
-[fastboot "INFO" responses, S = 0x0000]
-ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
-----------------------------------------------------------------------
-0x03  0x00  0x00  0x00  <command>
-                                        0x03  0x00  0x00  0x00
-0x03  0x00  0x00  0x01
-                                        0x03  0x00  0x00  0x01  INFOWait1
-0x03  0x00  0x00  0x02
-                                        0x03  0x00  0x00  0x02  INFOWait2
-0x03  0x00  0x00  0x03
-                                        0x03  0x00  0x00  0x03  OKAY
-
-----------------------------------------------------------------------
-[Chunking 2100 bytes of data, max packet size = 1024, S = 0xFFFF]
-ID   Flag SeqH SeqL Data                ID   Flag SeqH SeqL Data
-----------------------------------------------------------------------
-0x03 0x00 0xFF 0xFF download:0000834
-                                        0x03 0x00 0xFF 0xFF
-0x03 0x00 0x00 0x00
-                                        0x03 0x00 0x00 0x00 DATA0000834
-0x03 0x01 0x00 0x01 <1020 bytes>
-                                        0x03 0x00 0x00 0x01
-0x03 0x01 0x00 0x02 <1020 bytes>
-                                        0x03 0x00 0x00 0x02
-0x03 0x00 0x00 0x03 <60 bytes>
-                                        0x03 0x00 0x00 0x03
-0x03 0x00 0x00 0x04
-                                        0x03 0x00 0x00 0x04 OKAY
-
-----------------------------------------------------------------------
-[Unknown ID error, S = 0x0000]
-ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
-----------------------------------------------------------------------
-0x10  0x00  0x00  0x00
-                                        0x00  0x00  0x00  0x00  <error message>
-
-----------------------------------------------------------------------
-[Host packet loss and retransmission, S = 0x0000]
-ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
-----------------------------------------------------------------------
-0x03  0x00  0x00  0x00  getvar:version [lost]
-0x03  0x00  0x00  0x00  getvar:version [lost]
-0x03  0x00  0x00  0x00  getvar:version
-                                        0x03  0x00  0x00  0x00
-0x03  0x00  0x00  0x01
-                                        0x03  0x00  0x00  0x01  OKAY0.4
-
-----------------------------------------------------------------------
-[Client packet loss and retransmission, S = 0x0000]
-ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
-----------------------------------------------------------------------
-0x03  0x00  0x00  0x00  getvar:version
-                                        0x03  0x00  0x00  0x00 [lost]
-0x03  0x00  0x00  0x00  getvar:version
-                                        0x03  0x00  0x00  0x00 [lost]
-0x03  0x00  0x00  0x00  getvar:version
-                                        0x03  0x00  0x00  0x00
-0x03  0x00  0x00  0x01
-                                        0x03  0x00  0x00  0x01  OKAY0.4
-
-----------------------------------------------------------------------
-[Host packet delayed, S = 0x0000]
-ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
-----------------------------------------------------------------------
-0x03  0x00  0x00  0x00  getvar:version [delayed]
-0x03  0x00  0x00  0x00  getvar:version
-                                        0x03  0x00  0x00  0x00
-0x03  0x00  0x00  0x01
-                                        0x03  0x00  0x00  0x01  OKAY0.4
-0x03  0x00  0x00  0x00  getvar:version [arrives late with old seq#, is ignored]
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 9b73165..5d9ccfe 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -14,18 +14,21 @@
 #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)
+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(fd, partSize, NULL, NULL);
+        make_ext4fs_sparse_fd_align(fd, partSize, NULL, NULL, eraseBlkSize, logicalBlkSize);
     } else {
-        make_ext4fs_sparse_fd_directory(fd, partSize, NULL, NULL, initial_dir.c_str());
+        make_ext4fs_sparse_fd_directory_align(fd, partSize, NULL, NULL, initial_dir.c_str(),
+                                              eraseBlkSize, logicalBlkSize);
     }
     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(int fd, 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");
@@ -39,7 +42,8 @@
     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)(int fd, long long partSize, const std::string& initial_dir,
+                    unsigned eraseBlkSize, unsigned logicalBlkSize);
 
 } generators[] = {
     { "ext4", generate_ext4_image},
@@ -58,7 +62,7 @@
 }
 
 int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize,
-    const std::string& initial_dir)
+    const std::string& initial_dir, unsigned eraseBlkSize, unsigned logicalBlkSize)
 {
-    return gen->generate(tmpFileNo, partSize, initial_dir);
+    return gen->generate(tmpFileNo, partSize, initial_dir, eraseBlkSize, logicalBlkSize);
 }
diff --git a/fastboot/fs.h b/fastboot/fs.h
index 0a68507..0a5f5a4 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -8,6 +8,6 @@
 
 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,
-    const std::string& initial_dir);
+    const std::string& initial_dir, unsigned eraseBlkSize = 0, unsigned logicalBlkSize = 0);
 
 #endif
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index ee5d575..9069baa 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -92,7 +92,6 @@
     HRESULT result;
     SInt32 score;
     UInt8 interfaceNumEndpoints;
-    UInt8 configuration;
 
     // Placing the constant KIOUSBFindInterfaceDontCare into the following
     // fields of the IOUSBFindInterfaceRequest structure will allow us to
@@ -102,13 +101,6 @@
     request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
     request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
 
-    // SetConfiguration will kill an existing UMS connection, so let's
-    // not do this if not necessary.
-    configuration = 0;
-    (*dev)->GetConfiguration(dev, &configuration);
-    if (configuration != 1)
-        (*dev)->SetConfiguration(dev, 1);
-
     // Get an iterator for the interfaces on the device
     kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
 
@@ -310,13 +302,6 @@
 
     // So, we have a device, finally. Grab its vitals.
 
-
-    kr = (*dev)->USBDeviceOpen(dev);
-    if (kr != 0) {
-        WARN("USBDeviceOpen");
-        goto out;
-    }
-
     kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor);
     if (kr != 0) {
         ERR("GetDeviceVendor");
@@ -389,16 +374,12 @@
         goto error;
     }
 
-    out:
-
-    (*dev)->USBDeviceClose(dev);
     (*dev)->Release(dev);
     return 0;
 
     error:
 
     if (dev != NULL) {
-        (*dev)->USBDeviceClose(dev);
         (*dev)->Release(dev);
     }
 
diff --git a/fingerprintd/Android.mk b/fingerprintd/Android.mk
deleted file mode 100644
index 55803cf..0000000
--- a/fingerprintd/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)
-
-include $(CLEAR_VARS)
-LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused
-LOCAL_SRC_FILES := \
-	FingerprintDaemonProxy.cpp \
-	IFingerprintDaemon.cpp \
-	IFingerprintDaemonCallback.cpp \
-	fingerprintd.cpp
-LOCAL_MODULE := fingerprintd
-LOCAL_SHARED_LIBRARIES := \
-	libbinder \
-	libhidlbase \
-	libhidltransport \
-	liblog \
-	libhardware \
-	libutils \
-	libkeystore_binder \
-	android.hardware.biometrics.fingerprint@2.1
-include $(BUILD_EXECUTABLE)
diff --git a/fingerprintd/FingerprintDaemonProxy.cpp b/fingerprintd/FingerprintDaemonProxy.cpp
deleted file mode 100644
index 0119e55..0000000
--- a/fingerprintd/FingerprintDaemonProxy.cpp
+++ /dev/null
@@ -1,311 +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.
- */
-
-#define LOG_TAG "fingerprintd"
-
-#include <android/hardware/biometrics/fingerprint/2.1/types.h>
-#include <android/hardware/biometrics/fingerprint/2.1/IBiometricsFingerprint.h>
-#include <android/hardware/biometrics/fingerprint/2.1/IBiometricsFingerprintClientCallback.h>
-#include <binder/IServiceManager.h>
-#include <keystore/IKeystoreService.h>
-#include <keystore/keystore.h> // for error codes
-#include <utils/Log.h>
-
-#include "FingerprintDaemonProxy.h"
-
-namespace android {
-
-using hardware::hidl_string;
-using hardware::Return;
-using hardware::biometrics::fingerprint::V2_1::FingerprintMsg;
-using hardware::biometrics::fingerprint::V2_1::RequestStatus;
-using hardware::biometrics::fingerprint::V2_1::FingerprintError;
-using hardware::biometrics::fingerprint::V2_1::IBiometricsFingerprintClientCallback;
-using Type = hardware::biometrics::fingerprint::V2_1::FingerprintMsgType;
-using IBiometricsFingerprint = hardware::biometrics::fingerprint::V2_1::IBiometricsFingerprint;
-
-FingerprintDaemonProxy* FingerprintDaemonProxy::sInstance = nullptr;
-static sp<IBiometricsFingerprint> gBFP = nullptr;
-static sp<IBiometricsFingerprintClientCallback> gClientCallback = nullptr;
-
-template <typename E>
-constexpr typename std::underlying_type<E>::type to_native(E e) {
-    return static_cast<typename std::underlying_type<E>::type>(e);
-}
-
-const ssize_t hw_auth_token_size = 69;
-
-namespace hardware {
-
-class BiometricsFingerprintClientCallback : public IBiometricsFingerprintClientCallback {
-  public:
-    BiometricsFingerprintClientCallback() {};
-    virtual ~BiometricsFingerprintClientCallback() = default;
-    Return<void> notify(const FingerprintMsg& msg) {
-        FingerprintDaemonProxy::hal_notify_callback(msg);
-        return Void();
-    }
-};
-
-IBiometricsFingerprintClientCallback* HIDL_FETCH_IBiometricsFingerprintClientCallback(const char* /* name */) {
-    return new BiometricsFingerprintClientCallback();
-}
-
-} // namespace hardware
-
-FingerprintDaemonProxy::FingerprintDaemonProxy() : mCallback(nullptr) {
-
-}
-
-FingerprintDaemonProxy::~FingerprintDaemonProxy() {
-    closeHal();
-}
-
-void FingerprintDaemonProxy::hal_notify_callback(const hardware::biometrics::fingerprint::V2_1::FingerprintMsg &msg) {
-    FingerprintDaemonProxy* instance = FingerprintDaemonProxy::getInstance();
-    const sp<IFingerprintDaemonCallback> callback = instance->mCallback;
-    if (callback == nullptr) {
-        ALOGE("Invalid callback object");
-        return;
-    }
-    switch (msg.type) {
-        case Type::ERROR:
-            ALOGD("onError(%d)", msg.data.error);
-            callback->onError(0, to_native(msg.data.error));
-            break;
-        case Type::ACQUIRED:
-            ALOGD("onAcquired(%d)", msg.data.acquired.acquiredInfo);
-            callback->onAcquired(0, to_native(msg.data.acquired.acquiredInfo));
-            break;
-        case Type::AUTHENTICATED:
-            ALOGD("onAuthenticated(fid=%d, gid=%d)",
-                    msg.data.authenticated.finger.fid,
-                    msg.data.authenticated.finger.gid);
-            if (msg.data.authenticated.finger.fid != 0) {
-                const uint8_t* hat = reinterpret_cast<const uint8_t *>(&msg.data.authenticated.hat);
-                instance->notifyKeystore(hat, sizeof(msg.data.authenticated.hat));
-            }
-            callback->onAuthenticated(0,
-                    msg.data.authenticated.finger.fid,
-                    msg.data.authenticated.finger.gid);
-            break;
-        case Type::TEMPLATE_ENROLLING:
-            ALOGD("onEnrollResult(fid=%d, gid=%d, rem=%d)",
-                    msg.data.enroll.finger.fid,
-                    msg.data.enroll.finger.gid,
-                    msg.data.enroll.samplesRemaining);
-            callback->onEnrollResult(0,
-                    msg.data.enroll.finger.fid,
-                    msg.data.enroll.finger.gid,
-                    msg.data.enroll.samplesRemaining);
-            break;
-        case Type::TEMPLATE_REMOVED:
-            ALOGD("onRemove(fid=%d, gid=%d)",
-                    msg.data.removed.finger.fid,
-                    msg.data.removed.finger.gid);
-            callback->onRemoved(0,
-                    msg.data.removed.finger.fid,
-                    msg.data.removed.finger.gid);
-            break;
-        case Type::TEMPLATE_ENUMERATING:
-            ALOGD("onEnumerate(fid=%d, gid=%d, rem=%d)",
-                    msg.data.enumerated.finger.fid,
-                    msg.data.enumerated.finger.gid,
-                    msg.data.enumerated.remainingTemplates);
-            callback->onEnumerate(0,
-                    msg.data.enumerated.finger.fid,
-                    msg.data.enumerated.finger.gid,
-                    msg.data.enumerated.remainingTemplates);
-            break;
-        default:
-            ALOGE("invalid msg type: %d", msg.type);
-            return;
-    }
-}
-
-void FingerprintDaemonProxy::notifyKeystore(const uint8_t *auth_token, const size_t auth_token_length) {
-    if (auth_token != nullptr && auth_token_length > 0) {
-        // TODO: cache service?
-        sp < IServiceManager > sm = defaultServiceManager();
-        sp < IBinder > binder = sm->getService(String16("android.security.keystore"));
-        sp < IKeystoreService > service = interface_cast < IKeystoreService > (binder);
-        if (service != nullptr) {
-            status_t ret = service->addAuthToken(auth_token, auth_token_length);
-            if (ret != ResponseCode::NO_ERROR) {
-                ALOGE("Falure sending auth token to KeyStore: %d", ret);
-            }
-        } else {
-            ALOGE("Unable to communicate with KeyStore");
-        }
-    }
-}
-
-void FingerprintDaemonProxy::init(const sp<IFingerprintDaemonCallback>& callback) {
-    if (mCallback != nullptr && IInterface::asBinder(callback) != IInterface::asBinder(mCallback)) {
-        IInterface::asBinder(mCallback)->unlinkToDeath(this);
-    }
-    IInterface::asBinder(callback)->linkToDeath(this);
-    mCallback = callback;
-}
-
-int32_t FingerprintDaemonProxy::enroll(const uint8_t* token, ssize_t tokenSize, int32_t groupId,
-        int32_t timeout) {
-    ALOG(LOG_VERBOSE, LOG_TAG, "enroll(gid=%d, timeout=%d)\n", groupId, timeout);
-    if (tokenSize != hw_auth_token_size) {
-        ALOG(LOG_VERBOSE, LOG_TAG, "enroll() : invalid token size %zd, expected %zd\n", tokenSize, hw_auth_token_size);
-        return -1;
-    }
-
-    hardware::hidl_array<uint8_t, hw_auth_token_size> hat(token);
-    Return<RequestStatus> ret = gBFP->enroll(hat, groupId, timeout);
-    if (!ret.getStatus().isOk()) {
-        ALOGE("Unknown transport error");
-        return -1;
-    }
-
-    RequestStatus status = ret;
-    return to_native(status);
-}
-
-uint64_t FingerprintDaemonProxy::preEnroll() {
-    return gBFP->preEnroll();
-}
-
-int32_t FingerprintDaemonProxy::postEnroll() {
-    Return<RequestStatus> ret = gBFP->postEnroll();
-    if (!ret.getStatus().isOk()) {
-        ALOGE("Unknown transport error");
-        return -1;
-    }
-
-    RequestStatus status = ret;
-    return to_native(status);
-}
-
-int32_t FingerprintDaemonProxy::stopEnrollment() {
-    ALOG(LOG_VERBOSE, LOG_TAG, "stopEnrollment()\n");
-    Return<RequestStatus> ret = gBFP->cancel();
-    if (!ret.getStatus().isOk()) {
-        ALOGE("Unknown transport error");
-        return -1;
-    }
-
-    RequestStatus status = ret;
-    return to_native(status);
-}
-
-int32_t FingerprintDaemonProxy::authenticate(uint64_t sessionId, uint32_t groupId) {
-    ALOG(LOG_VERBOSE, LOG_TAG, "authenticate(sid=%" PRId64 ", gid=%d)\n", sessionId, groupId);
-    Return<RequestStatus> ret = gBFP->authenticate(sessionId, groupId);
-    if (!ret.getStatus().isOk()) {
-        ALOGE("Unknown transport error");
-        return -1;
-    }
-
-    RequestStatus status = ret;
-    return to_native(status);
-}
-
-int32_t FingerprintDaemonProxy::stopAuthentication() {
-    ALOG(LOG_VERBOSE, LOG_TAG, "stopAuthentication()\n");
-    Return<RequestStatus> ret = gBFP->cancel();
-    if (!ret.getStatus().isOk()) {
-        ALOGE("Unknown transport error");
-        return -1;
-    }
-
-    RequestStatus status = ret;
-    return to_native(status);
-}
-
-int32_t FingerprintDaemonProxy::remove(int32_t fingerId, int32_t groupId) {
-    ALOG(LOG_VERBOSE, LOG_TAG, "remove(fid=%d, gid=%d)\n", fingerId, groupId);
-    Return<RequestStatus> ret = gBFP->remove(groupId, fingerId);
-    if (!ret.getStatus().isOk()) {
-        ALOGE("Unknown transport error");
-        return -1;
-    }
-
-    RequestStatus status = ret;
-    return to_native(status);
-}
-
-int32_t FingerprintDaemonProxy::enumerate() {
-    ALOG(LOG_VERBOSE, LOG_TAG, "enumerate()\n");
-    Return<RequestStatus> ret = gBFP->enumerate();
-    if (!ret.getStatus().isOk()) {
-        ALOGE("Unknown transport error");
-        return -1;
-    }
-
-    RequestStatus status = ret;
-    return to_native(status);
-}
-
-uint64_t FingerprintDaemonProxy::getAuthenticatorId() {
-    return gBFP->getAuthenticatorId();
-}
-
-int32_t FingerprintDaemonProxy::setActiveGroup(int32_t groupId, const uint8_t* path,
-        ssize_t pathlen) {
-    if (pathlen >= PATH_MAX || pathlen <= 0) {
-        ALOGE("Bad path length: %zd", pathlen);
-        return -1;
-    }
-    hidl_string pathname;
-    pathname.setToExternal(reinterpret_cast<const char*>(path), pathlen);
-    ALOG(LOG_VERBOSE, LOG_TAG, "setActiveGroup(%d, %s, %zu)", groupId, pathname.c_str(), pathlen);
-    Return<RequestStatus> ret = gBFP->setActiveGroup(groupId, pathname);
-    if (!ret.getStatus().isOk()) {
-        ALOGE("Unknown transport error");
-        return -1;
-    }
-
-    RequestStatus status = ret;
-    return to_native(status);
-}
-
-int64_t FingerprintDaemonProxy::openHal() {
-    if (gBFP == nullptr) {
-        // TODO(b/31632518)
-        gBFP = IBiometricsFingerprint::getService("fingerprint");
-        if(gBFP == nullptr) {
-            ALOGE("Can't get service fingerprint");
-            return 0;
-        }
-    }
-    gClientCallback = hardware::HIDL_FETCH_IBiometricsFingerprintClientCallback(nullptr);
-    gBFP->setNotify(gClientCallback);
-    return reinterpret_cast<int64_t>(gBFP.get());
-}
-
-int32_t FingerprintDaemonProxy::closeHal() {
-    // Obsolete, return 0 for compatibility reasons.
-    return 0;
-}
-
-void FingerprintDaemonProxy::binderDied(const wp<IBinder>& who) {
-    ALOGD("binder died");
-    int err;
-    if (0 != (err = closeHal())) {
-        ALOGE("Can't close fingerprint device, error: %d", err);
-    }
-    if (IInterface::asBinder(mCallback) == who) {
-        mCallback = nullptr;
-    }
-}
-
-} // namespace android
diff --git a/fingerprintd/FingerprintDaemonProxy.h b/fingerprintd/FingerprintDaemonProxy.h
deleted file mode 100644
index 715344c..0000000
--- a/fingerprintd/FingerprintDaemonProxy.h
+++ /dev/null
@@ -1,65 +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.
- */
-
-#ifndef FINGERPRINT_DAEMON_PROXY_H_
-#define FINGERPRINT_DAEMON_PROXY_H_
-
-#include "IFingerprintDaemon.h"
-#include "IFingerprintDaemonCallback.h"
-
-namespace android {
-
-using hardware::biometrics::fingerprint::V2_1::RequestStatus;
-
-class FingerprintDaemonProxy : public BnFingerprintDaemon {
-    public:
-        static FingerprintDaemonProxy* getInstance() {
-            if (sInstance == NULL) {
-                sInstance = new FingerprintDaemonProxy();
-            }
-            return sInstance;
-        }
-
-        // These reflect binder methods.
-        virtual void init(const sp<IFingerprintDaemonCallback>& callback);
-        virtual int32_t enroll(const uint8_t* token, ssize_t tokenLength, int32_t groupId, int32_t timeout);
-        virtual uint64_t preEnroll();
-        virtual int32_t postEnroll();
-        virtual int32_t stopEnrollment();
-        virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId);
-        virtual int32_t stopAuthentication();
-        virtual int32_t remove(int32_t fingerId, int32_t groupId);
-        virtual int32_t enumerate();
-        virtual uint64_t getAuthenticatorId();
-        virtual int32_t setActiveGroup(int32_t groupId, const uint8_t* path, ssize_t pathLen);
-        virtual int64_t openHal();
-        virtual int32_t closeHal();
-        static void hal_notify_callback(const hardware::biometrics::fingerprint::V2_1::FingerprintMsg &msg);
-
-    private:
-        FingerprintDaemonProxy();
-        virtual ~FingerprintDaemonProxy();
-        void binderDied(const wp<IBinder>& who);
-        void notifyKeystore(const uint8_t *auth_token, const size_t auth_token_length);
-        int32_t HandleTransportError(const RequestStatus error);
-
-        static FingerprintDaemonProxy* sInstance;
-        sp<IFingerprintDaemonCallback> mCallback;
-};
-
-} // namespace android
-
-#endif // FINGERPRINT_DAEMON_PROXY_H_
diff --git a/fingerprintd/IFingerprintDaemon.cpp b/fingerprintd/IFingerprintDaemon.cpp
deleted file mode 100644
index bc4af56..0000000
--- a/fingerprintd/IFingerprintDaemon.cpp
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * 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 <inttypes.h>
-
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/PermissionCache.h>
-#include <utils/String16.h>
-#include <utils/Looper.h>
-#include <keystore/IKeystoreService.h>
-#include <keystore/keystore.h> // for error code
-#include <hardware/hardware.h>
-#include <hardware/fingerprint.h>
-#include <hardware/hw_auth_token.h>
-#include "IFingerprintDaemon.h"
-#include "IFingerprintDaemonCallback.h"
-
-namespace android {
-
-static const String16 USE_FINGERPRINT_PERMISSION("android.permission.USE_FINGERPRINT");
-static const String16 MANAGE_FINGERPRINT_PERMISSION("android.permission.MANAGE_FINGERPRINT");
-static const String16 HAL_FINGERPRINT_PERMISSION("android.permission.MANAGE_FINGERPRINT"); // TODO
-static const String16 DUMP_PERMISSION("android.permission.DUMP");
-
-const android::String16
-IFingerprintDaemon::descriptor("android.hardware.fingerprint.IFingerprintDaemon");
-
-const android::String16&
-IFingerprintDaemon::getInterfaceDescriptor() const {
-    return IFingerprintDaemon::descriptor;
-}
-
-status_t BnFingerprintDaemon::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-        uint32_t flags) {
-    switch(code) {
-        case AUTHENTICATE: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const uint64_t sessionId = data.readInt64();
-            const uint32_t groupId = data.readInt32();
-            const int32_t ret = authenticate(sessionId, groupId);
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        };
-        case CANCEL_AUTHENTICATION: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const int32_t ret = stopAuthentication();
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        }
-        case ENROLL: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const ssize_t tokenSize = data.readInt32();
-            const uint8_t* token = static_cast<const uint8_t *>(data.readInplace(tokenSize));
-            const int32_t groupId = data.readInt32();
-            const int32_t timeout = data.readInt32();
-            const int32_t ret = enroll(token, tokenSize, groupId, timeout);
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        }
-        case CANCEL_ENROLLMENT: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const int32_t ret = stopEnrollment();
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        }
-        case PRE_ENROLL: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const uint64_t ret = preEnroll();
-            reply->writeNoException();
-            reply->writeInt64(ret);
-            return NO_ERROR;
-        }
-        case POST_ENROLL: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const int32_t ret = postEnroll();
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        }
-        case REMOVE: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const int32_t fingerId = data.readInt32();
-            const int32_t groupId = data.readInt32();
-            const int32_t ret = remove(fingerId, groupId);
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        }
-        case ENUMERATE: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const int32_t ret = enumerate();
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        }
-        case GET_AUTHENTICATOR_ID: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const uint64_t ret = getAuthenticatorId();
-            reply->writeNoException();
-            reply->writeInt64(ret);
-            return NO_ERROR;
-        }
-        case SET_ACTIVE_GROUP: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const int32_t group = data.readInt32();
-            const ssize_t pathSize = data.readInt32();
-            const uint8_t* path = static_cast<const uint8_t *>(data.readInplace(pathSize));
-            const int32_t ret = setActiveGroup(group, path, pathSize);
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        }
-        case OPEN_HAL: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const int64_t ret = openHal();
-            reply->writeNoException();
-            reply->writeInt64(ret);
-            return NO_ERROR;
-        }
-        case CLOSE_HAL: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const int32_t ret = closeHal();
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        }
-        case INIT: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            sp<IFingerprintDaemonCallback> callback =
-                    interface_cast<IFingerprintDaemonCallback>(data.readStrongBinder());
-            init(callback);
-            reply->writeNoException();
-            return NO_ERROR;
-        }
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
-    }
-};
-
-bool BnFingerprintDaemon::checkPermission(const String16& permission) {
-    const IPCThreadState* ipc = IPCThreadState::self();
-    const int calling_pid = ipc->getCallingPid();
-    const int calling_uid = ipc->getCallingUid();
-    return PermissionCache::checkPermission(permission, calling_pid, calling_uid);
-}
-
-
-}; // namespace android
diff --git a/fingerprintd/IFingerprintDaemon.h b/fingerprintd/IFingerprintDaemon.h
deleted file mode 100644
index c5cdbfe..0000000
--- a/fingerprintd/IFingerprintDaemon.h
+++ /dev/null
@@ -1,90 +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.
- */
-
-#ifndef IFINGERPRINT_DAEMON_H_
-#define IFINGERPRINT_DAEMON_H_
-
-#include <android/hardware/biometrics/fingerprint/2.1/IBiometricsFingerprint.h>
-#include <android/hardware/biometrics/fingerprint/2.1/types.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-class IFingerprintDaemonCallback;
-
-/*
-* Abstract base class for native implementation of FingerprintService.
-*
-* Note: This must be kept manually in sync with IFingerprintDaemon.aidl
-*/
-class IFingerprintDaemon : public IInterface, public IBinder::DeathRecipient {
-    public:
-        enum {
-           AUTHENTICATE = IBinder::FIRST_CALL_TRANSACTION + 0,
-           CANCEL_AUTHENTICATION = IBinder::FIRST_CALL_TRANSACTION + 1,
-           ENROLL = IBinder::FIRST_CALL_TRANSACTION + 2,
-           CANCEL_ENROLLMENT = IBinder::FIRST_CALL_TRANSACTION + 3,
-           PRE_ENROLL = IBinder::FIRST_CALL_TRANSACTION + 4,
-           REMOVE = IBinder::FIRST_CALL_TRANSACTION + 5,
-           GET_AUTHENTICATOR_ID = IBinder::FIRST_CALL_TRANSACTION + 6,
-           SET_ACTIVE_GROUP = IBinder::FIRST_CALL_TRANSACTION + 7,
-           OPEN_HAL = IBinder::FIRST_CALL_TRANSACTION + 8,
-           CLOSE_HAL = IBinder::FIRST_CALL_TRANSACTION + 9,
-           INIT = IBinder::FIRST_CALL_TRANSACTION + 10,
-           POST_ENROLL = IBinder::FIRST_CALL_TRANSACTION + 11,
-           ENUMERATE = IBinder::FIRST_CALL_TRANSACTION + 12,
-        };
-
-        IFingerprintDaemon() { }
-        virtual ~IFingerprintDaemon() { }
-        virtual const android::String16& getInterfaceDescriptor() const;
-
-        // Binder interface methods
-        virtual void init(const sp<IFingerprintDaemonCallback>& callback) = 0;
-        virtual int32_t enroll(const uint8_t* token, ssize_t tokenLength, int32_t groupId,
-                int32_t timeout) = 0;
-        virtual uint64_t preEnroll() = 0;
-        virtual int32_t postEnroll() = 0;
-        virtual int32_t stopEnrollment() = 0;
-        virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId) = 0;
-        virtual int32_t stopAuthentication() = 0;
-        virtual int32_t remove(int32_t fingerId, int32_t groupId) = 0;
-        virtual int32_t enumerate() = 0;
-        virtual uint64_t getAuthenticatorId() = 0;
-        virtual int32_t setActiveGroup(int32_t groupId, const uint8_t* path, ssize_t pathLen) = 0;
-        virtual int64_t openHal() = 0;
-        virtual int32_t closeHal() = 0;
-
-        // DECLARE_META_INTERFACE - C++ client interface not needed
-        static const android::String16 descriptor;
-        static void hal_notify_callback(const hardware::biometrics::fingerprint::V2_1::FingerprintMsg &msg);
-};
-
-// ----------------------------------------------------------------------------
-
-class BnFingerprintDaemon: public BnInterface<IFingerprintDaemon> {
-    public:
-       virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-               uint32_t flags = 0);
-    private:
-       bool checkPermission(const String16& permission);
-};
-
-} // namespace android
-
-#endif // IFINGERPRINT_DAEMON_H_
-
diff --git a/fingerprintd/IFingerprintDaemonCallback.cpp b/fingerprintd/IFingerprintDaemonCallback.cpp
deleted file mode 100644
index 1d75aa7..0000000
--- a/fingerprintd/IFingerprintDaemonCallback.cpp
+++ /dev/null
@@ -1,91 +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.
- */
-
-#define LOG_TAG "IFingerprintDaemonCallback"
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/Log.h>
-#include <binder/Parcel.h>
-
-#include "IFingerprintDaemonCallback.h"
-
-namespace android {
-
-class BpFingerprintDaemonCallback : public BpInterface<IFingerprintDaemonCallback>
-{
-public:
-    explicit BpFingerprintDaemonCallback(const sp<IBinder>& impl) :
-            BpInterface<IFingerprintDaemonCallback>(impl) {
-    }
-    virtual status_t onEnrollResult(int64_t devId, int32_t fpId, int32_t gpId, int32_t rem) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
-        data.writeInt64(devId);
-        data.writeInt32(fpId);
-        data.writeInt32(gpId);
-        data.writeInt32(rem);
-        return remote()->transact(ON_ENROLL_RESULT, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-
-    virtual status_t onAcquired(int64_t devId, int32_t acquiredInfo) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
-        data.writeInt64(devId);
-        data.writeInt32(acquiredInfo);
-        return remote()->transact(ON_ACQUIRED, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-
-    virtual status_t onAuthenticated(int64_t devId, int32_t fpId, int32_t gpId) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
-        data.writeInt64(devId);
-        data.writeInt32(fpId);
-        data.writeInt32(gpId);
-        return remote()->transact(ON_AUTHENTICATED, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-
-    virtual status_t onError(int64_t devId, int32_t error) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
-        data.writeInt64(devId);
-        data.writeInt32(error);
-        return remote()->transact(ON_ERROR, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-
-    virtual status_t onRemoved(int64_t devId, int32_t fpId, int32_t gpId) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
-        data.writeInt64(devId);
-        data.writeInt32(fpId);
-        data.writeInt32(gpId);
-        return remote()->transact(ON_REMOVED, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-
-    virtual status_t onEnumerate(int64_t devId, int32_t fpId, int32_t gpId, int32_t rem) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
-        data.writeInt64(devId);
-        data.writeInt32(fpId);
-        data.writeInt32(gpId);
-        data.writeInt32(rem);
-        return remote()->transact(ON_ENUMERATE, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-};
-
-IMPLEMENT_META_INTERFACE(FingerprintDaemonCallback,
-        "android.hardware.fingerprint.IFingerprintDaemonCallback");
-
-}; // namespace android
diff --git a/fingerprintd/IFingerprintDaemonCallback.h b/fingerprintd/IFingerprintDaemonCallback.h
deleted file mode 100644
index e343cb4..0000000
--- a/fingerprintd/IFingerprintDaemonCallback.h
+++ /dev/null
@@ -1,54 +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.
- */
-
-#ifndef IFINGERPRINT_DAEMON_CALLBACK_H_
-#define IFINGERPRINT_DAEMON_CALLBACK_H_
-
-#include <inttypes.h>
-#include <utils/Errors.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-/*
-* Communication channel back to FingerprintService.java
-*/
-class IFingerprintDaemonCallback : public IInterface {
-    public:
-        // must be kept in sync with IFingerprintService.aidl
-        enum {
-            ON_ENROLL_RESULT = IBinder::FIRST_CALL_TRANSACTION + 0,
-            ON_ACQUIRED = IBinder::FIRST_CALL_TRANSACTION + 1,
-            ON_AUTHENTICATED = IBinder::FIRST_CALL_TRANSACTION + 2,
-            ON_ERROR = IBinder::FIRST_CALL_TRANSACTION + 3,
-            ON_REMOVED = IBinder::FIRST_CALL_TRANSACTION + 4,
-            ON_ENUMERATE = IBinder::FIRST_CALL_TRANSACTION + 5,
-        };
-
-        virtual status_t onEnrollResult(int64_t devId, int32_t fpId, int32_t gpId, int32_t rem) = 0;
-        virtual status_t onAcquired(int64_t devId, int32_t acquiredInfo) = 0;
-        virtual status_t onAuthenticated(int64_t devId, int32_t fingerId, int32_t groupId) = 0;
-        virtual status_t onError(int64_t devId, int32_t error) = 0;
-        virtual status_t onRemoved(int64_t devId, int32_t fingerId, int32_t groupId) = 0;
-        virtual status_t onEnumerate(int64_t devId, int32_t fingerId, int32_t groupId, int32_t rem) = 0;
-
-        DECLARE_META_INTERFACE(FingerprintDaemonCallback);
-};
-
-}; // namespace android
-
-#endif // IFINGERPRINT_DAEMON_CALLBACK_H_
diff --git a/fingerprintd/fingerprintd.cpp b/fingerprintd/fingerprintd.cpp
deleted file mode 100644
index 05109b7..0000000
--- a/fingerprintd/fingerprintd.cpp
+++ /dev/null
@@ -1,52 +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.
- */
-
-#define LOG_TAG "fingerprintd"
-
-#include <android/log.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/PermissionCache.h>
-#include <hardware/hardware.h>
-#include <hardware/fingerprint.h>
-#include <hardware/hw_auth_token.h>
-#include <keystore/IKeystoreService.h>
-#include <keystore/keystore.h> // for error codes
-#include <utils/Log.h>
-#include <utils/String16.h>
-
-#include "FingerprintDaemonProxy.h"
-
-int main() {
-    ALOGI("Starting " LOG_TAG);
-    android::sp<android::IServiceManager> serviceManager = android::defaultServiceManager();
-    android::sp<android::FingerprintDaemonProxy> proxy =
-            android::FingerprintDaemonProxy::getInstance();
-    android::status_t ret = serviceManager->addService(
-            android::FingerprintDaemonProxy::descriptor, proxy);
-    if (ret != android::OK) {
-        ALOGE("Couldn't register " LOG_TAG " binder service!");
-        return -1;
-    }
-
-    /*
-     * We're the only thread in existence, so we're just going to process
-     * Binder transaction as a single-threaded program.
-     */
-    android::IPCThreadState::self()->joinThreadPool();
-    ALOGI("Done");
-    return 0;
-}
diff --git a/fs_mgr/.clang-format b/fs_mgr/.clang-format
new file mode 120000
index 0000000..4e6d9dd
--- /dev/null
+++ b/fs_mgr/.clang-format
@@ -0,0 +1 @@
+../init/.clang-format
\ No newline at end of file
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index d6b699b..2863a26 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -9,25 +9,28 @@
     libbase \
     libcrypto_utils \
     libcrypto \
-    libext4_utils_static \
+    libext4_utils \
     libsquashfs_utils \
-    libselinux
+    libselinux \
+    libavb
 
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
 LOCAL_SANITIZE := integer
 LOCAL_SRC_FILES:= \
-    fs_mgr.c \
-    fs_mgr_format.c \
-    fs_mgr_fstab.c \
-    fs_mgr_slotselect.c \
-    fs_mgr_verity.cpp
+    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 \
-    external/openssl/include \
-    bootable/recovery
+    system/extras/ext4_utils
 LOCAL_MODULE:= libfs_mgr
 LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
@@ -35,12 +38,15 @@
 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.c
+LOCAL_SRC_FILES:= fs_mgr_main.cpp
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_MODULE:= fs_mgr
 LOCAL_MODULE_TAGS := optional
@@ -52,7 +58,7 @@
     libcutils \
     liblog \
     libc \
-    libsparse_static \
+    libsparse \
     libz \
     libselinux
 LOCAL_CXX_STL := libc++_static
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.cpp
similarity index 66%
rename from fs_mgr/fs_mgr.c
rename to fs_mgr/fs_mgr.cpp
index f682216..a50817e 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.cpp
@@ -31,6 +31,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <android-base/unique_fd.h>
 #include <cutils/android_reboot.h>
 #include <cutils/partition_utils.h>
 #include <cutils/properties.h>
@@ -46,7 +47,7 @@
 #include <private/android_logger.h>
 
 #include "fs_mgr_priv.h"
-#include "fs_mgr_priv_verity.h"
+#include "fs_mgr_priv_avb.h"
 
 #define KEY_LOC_PROP   "ro.crypto.keyfile.userdata"
 #define KEY_IN_FOOTER  "footer"
@@ -74,7 +75,7 @@
 
     ret = clock_gettime(CLOCK_MONOTONIC, &ts);
     if (ret < 0) {
-        ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno));
+        PERROR << "clock_gettime(CLOCK_MONOTONIC) failed";
         return 0;
     }
 
@@ -93,13 +94,13 @@
     return ret;
 }
 
-static void check_fs(char *blk_device, char *fs_type, char *target)
+static void check_fs(const char *blk_device, char *fs_type, char *target)
 {
     int status;
     int ret;
     long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
     char tmpmnt_opts[64] = "errors=remount-ro";
-    char *e2fsck_argv[] = {
+    const char *e2fsck_argv[] = {
         E2FSCK_BIN,
         "-f",
         "-y",
@@ -127,8 +128,8 @@
             strlcat(tmpmnt_opts, ",nomblk_io_submit", sizeof(tmpmnt_opts));
         }
         ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
-        INFO("%s(): mount(%s,%s,%s)=%d: %s\n",
-             __func__, blk_device, target, fs_type, ret, strerror(errno));
+        PINFO << __FUNCTION__ << "(): mount(" << blk_device <<  "," << target
+              << "," << fs_type << ")=" << ret;
         if (!ret) {
             int i;
             for (i = 0; i < 5; i++) {
@@ -136,10 +137,12 @@
                 // Should we try rebooting if all attempts fail?
                 int result = umount(target);
                 if (result == 0) {
-                    INFO("%s(): unmount(%s) succeeded\n", __func__, target);
+                    LINFO << __FUNCTION__ << "(): unmount(" << target
+                          << ") succeeded";
                     break;
                 }
-                ERROR("%s(): umount(%s)=%d: %s\n", __func__, target, result, strerror(errno));
+                PERROR << __FUNCTION__ << "(): umount(" << target << ")="
+                       << result;
                 sleep(1);
             }
         }
@@ -149,34 +152,39 @@
          * (e.g. recent SDK system images). Detect these and skip the check.
          */
         if (access(E2FSCK_BIN, X_OK)) {
-            INFO("Not running %s on %s (executable not in system image)\n",
-                 E2FSCK_BIN, blk_device);
+            LINFO << "Not running " << E2FSCK_BIN << " on " << blk_device
+                  << " (executable not in system image)";
         } else {
-            INFO("Running %s on %s\n", E2FSCK_BIN, blk_device);
+            LINFO << "Running " << E2FSCK_BIN << " on " << blk_device;
 
-            ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv,
+            ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv),
+                                          const_cast<char **>(e2fsck_argv),
                                           &status, true, LOG_KLOG | LOG_FILE,
-                                          true, FSCK_LOG_FILE, NULL, 0);
+                                          true,
+                                          const_cast<char *>(FSCK_LOG_FILE),
+                                          NULL, 0);
 
             if (ret < 0) {
                 /* No need to check for error in fork, we can't really handle it now */
-                ERROR("Failed trying to run %s\n", E2FSCK_BIN);
+                LERROR << "Failed trying to run " << E2FSCK_BIN;
             }
         }
     } else if (!strcmp(fs_type, "f2fs")) {
-            char *f2fs_fsck_argv[] = {
+            const char *f2fs_fsck_argv[] = {
                     F2FS_FSCK_BIN,
                     "-a",
                     blk_device
             };
-        INFO("Running %s -a %s\n", F2FS_FSCK_BIN, blk_device);
+        LINFO << "Running " << F2FS_FSCK_BIN << " -a " << blk_device;
 
-        ret = android_fork_execvp_ext(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv,
+        ret = android_fork_execvp_ext(ARRAY_SIZE(f2fs_fsck_argv),
+                                      const_cast<char **>(f2fs_fsck_argv),
                                       &status, true, LOG_KLOG | LOG_FILE,
-                                      true, FSCK_LOG_FILE, NULL, 0);
+                                      true, const_cast<char *>(FSCK_LOG_FILE),
+                                      NULL, 0);
         if (ret < 0) {
             /* No need to check for error in fork, we can't really handle it now */
-            ERROR("Failed trying to run %s\n", F2FS_FSCK_BIN);
+            LERROR << "Failed trying to run " << F2FS_FSCK_BIN;
         }
     }
 
@@ -213,6 +221,73 @@
             le32_to_cpu(es->s_r_blocks_count_lo);
 }
 
+static int do_quota(char *blk_device, char *fs_type, struct fstab_rec *rec)
+{
+    int force_check = 0;
+    if (!strcmp(fs_type, "ext4")) {
+        /*
+         * Some system images do not have tune2fs for licensing reasons
+         * Detect these and skip reserve blocks.
+         */
+        if (access(TUNE2FS_BIN, X_OK)) {
+            LERROR << "Not running " << TUNE2FS_BIN << " on "
+                   << blk_device << " (executable not in system image)";
+        } else {
+            const char* arg1 = nullptr;
+            const char* arg2 = nullptr;
+            int status = 0;
+            int ret = 0;
+            android::base::unique_fd fd(
+                TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
+            if (fd >= 0) {
+                struct ext4_super_block sb;
+                ret = read_super_block(fd, &sb);
+                if (ret < 0) {
+                    PERROR << "Can't read '" << blk_device << "' super block";
+                    return force_check;
+                }
+
+                int has_quota = (sb.s_feature_ro_compat
+                        & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
+                int want_quota = fs_mgr_is_quota(rec) != 0;
+
+                if (has_quota == want_quota) {
+                    LINFO << "Requested quota status is match on " << blk_device;
+                    return force_check;
+                } else if (want_quota) {
+                    LINFO << "Enabling quota on " << blk_device;
+                    arg1 = "-Oquota";
+                    arg2 = "-Qusrquota,grpquota";
+                    force_check = 1;
+                } else {
+                    LINFO << "Disabling quota on " << blk_device;
+                    arg1 = "-Q^usrquota,^grpquota";
+                    arg2 = "-O^quota";
+                }
+            } else {
+                PERROR << "Failed to open '" << blk_device << "'";
+                return force_check;
+            }
+
+            const char *tune2fs_argv[] = {
+                TUNE2FS_BIN,
+                arg1,
+                arg2,
+                blk_device,
+            };
+            ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv),
+                                          const_cast<char **>(tune2fs_argv),
+                                          &status, true, LOG_KLOG | LOG_FILE,
+                                          true, NULL, NULL, 0);
+            if (ret < 0) {
+                /* No need to check for error in fork, we can't really handle it now */
+                LERROR << "Failed trying to run " << TUNE2FS_BIN;
+            }
+        }
+    }
+    return force_check;
+}
+
 static void do_reserved_size(char *blk_device, char *fs_type, struct fstab_rec *rec)
 {
     /* Check for the types of filesystems we know how to check */
@@ -222,56 +297,57 @@
          * Detect these and skip reserve blocks.
          */
         if (access(TUNE2FS_BIN, X_OK)) {
-            ERROR("Not running %s on %s (executable not in system image)\n",
-                  TUNE2FS_BIN, blk_device);
+            LERROR << "Not running " << TUNE2FS_BIN << " on "
+                   << blk_device << " (executable not in system image)";
         } else {
-            INFO("Running %s on %s\n", TUNE2FS_BIN, blk_device);
+            LINFO << "Running " << TUNE2FS_BIN << " on " << blk_device;
 
             int status = 0;
             int ret = 0;
             unsigned long reserved_blocks = 0;
-            int fd = TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC));
+            android::base::unique_fd fd(
+                TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
             if (fd >= 0) {
                 struct ext4_super_block sb;
                 ret = read_super_block(fd, &sb);
                 if (ret < 0) {
-                    ERROR("Can't read '%s' super block: %s\n", blk_device, strerror(errno));
-                    goto out;
+                    PERROR << "Can't read '" << blk_device << "' super block";
+                    return;
                 }
                 reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(&sb);
                 unsigned long reserved_threshold = ext4_blocks_count(&sb) * 0.02;
                 if (reserved_threshold < reserved_blocks) {
-                    WARNING("Reserved blocks %lu is too large\n", reserved_blocks);
+                    LWARNING << "Reserved blocks " << reserved_blocks
+                             << " is too large";
                     reserved_blocks = reserved_threshold;
                 }
 
                 if (ext4_r_blocks_count(&sb) == reserved_blocks) {
-                    INFO("Have reserved same blocks\n");
-                    goto out;
+                    LINFO << "Have reserved same blocks";
+                    return;
                 }
             } else {
-                ERROR("Failed to open '%s': %s\n", blk_device, strerror(errno));
+                PERROR << "Failed to open '" << blk_device << "'";
                 return;
             }
 
             char buf[16] = {0};
             snprintf(buf, sizeof (buf), "-r %lu", reserved_blocks);
-            char *tune2fs_argv[] = {
+            const char *tune2fs_argv[] = {
                 TUNE2FS_BIN,
                 buf,
                 blk_device,
             };
 
-            ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv), tune2fs_argv,
+            ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv),
+                                          const_cast<char **>(tune2fs_argv),
                                           &status, true, LOG_KLOG | LOG_FILE,
                                           true, NULL, NULL, 0);
 
             if (ret < 0) {
                 /* No need to check for error in fork, we can't really handle it now */
-                ERROR("Failed trying to run %s\n", TUNE2FS_BIN);
+                LERROR << "Failed trying to run " << TUNE2FS_BIN;
             }
-      out:
-            close(fd);
         }
     }
 }
@@ -330,7 +406,8 @@
     mkdir(target, 0755);
     ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
     save_errno = errno;
-    INFO("%s(source=%s,target=%s,type=%s)=%d\n", __func__, source, target, rec->fs_type, ret);
+    LINFO << __FUNCTION__ << "(source=" << source << ",target="
+          << target << ",type=" << rec->fs_type << ")=" << ret;
     if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
         fs_mgr_set_blk_ro(source);
     }
@@ -358,16 +435,6 @@
     return ret;
 }
 
-static int device_is_secure() {
-    int ret = -1;
-    char value[PROP_VALUE_MAX];
-    ret = __system_property_get("ro.secure", value);
-    /* If error, we want to fail secure */
-    if (ret < 0)
-        return 1;
-    return strcmp(value, "0") ? 1 : 0;
-}
-
 static int device_is_force_encrypted() {
     int ret = -1;
     char value[PROP_VALUE_MAX];
@@ -397,7 +464,7 @@
     if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) {
       errno = EINVAL;
       if (end_idx) *end_idx = start_idx;
-      if (attempted_idx) *end_idx = start_idx;
+      if (attempted_idx) *attempted_idx = start_idx;
       return -1;
     }
 
@@ -412,12 +479,18 @@
              * each other.
              */
             if (mounted) {
-                ERROR("%s(): skipping fstab dup mountpoint=%s rec[%d].fs_type=%s already mounted as %s.\n", __func__,
-                     fstab->recs[i].mount_point, i, fstab->recs[i].fs_type, fstab->recs[*attempted_idx].fs_type);
+                LERROR << __FUNCTION__ << "(): skipping fstab dup mountpoint="
+                       << fstab->recs[i].mount_point << " rec[" << i
+                       << "].fs_type=" << fstab->recs[i].fs_type
+                       << " already mounted as "
+                       << fstab->recs[*attempted_idx].fs_type;
                 continue;
             }
 
-            if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
+            int force_check = do_quota(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+                                       &fstab->recs[i]);
+
+            if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
                 check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
                          fstab->recs[i].mount_point);
             }
@@ -431,9 +504,11 @@
                 *attempted_idx = i;
                 mounted = 1;
                 if (i != start_idx) {
-                    ERROR("%s(): Mounted %s on %s with fs_type=%s instead of %s\n", __func__,
-                         fstab->recs[i].blk_device, fstab->recs[i].mount_point, fstab->recs[i].fs_type,
-                         fstab->recs[start_idx].fs_type);
+                    LERROR << __FUNCTION__ << "(): Mounted "
+                           << fstab->recs[i].blk_device << " on "
+                           << fstab->recs[i].mount_point << " with fs_type="
+                           << fstab->recs[i].fs_type << " instead of "
+                           << fstab->recs[start_idx].fs_type;
                 }
             } else {
                 /* back up errno for crypto decisions */
@@ -468,14 +543,14 @@
     label_len = strlen(label);
 
     if (label_len > 16) {
-        ERROR("FS label is longer than allowed by filesystem\n");
+        LERROR << "FS label is longer than allowed by filesystem";
         goto out;
     }
 
 
     blockdir = opendir("/dev/block");
     if (!blockdir) {
-        ERROR("couldn't open /dev/block\n");
+        LERROR << "couldn't open /dev/block";
         goto out;
     }
 
@@ -489,7 +564,7 @@
 
         fd = openat(dirfd(blockdir), ent->d_name, O_RDONLY);
         if (fd < 0) {
-            ERROR("Cannot open block device /dev/block/%s\n", ent->d_name);
+            LERROR << "Cannot open block device /dev/block/" << ent->d_name;
             goto out;
         }
 
@@ -504,7 +579,7 @@
 
         sb = (struct ext4_super_block *)super_buf;
         if (sb->s_magic != EXT4_SUPER_MAGIC) {
-            INFO("/dev/block/%s not ext{234}\n", ent->d_name);
+            LINFO << "/dev/block/" << ent->d_name << " not ext{234}";
             continue;
         }
 
@@ -512,11 +587,12 @@
             char *new_blk_device;
 
             if (asprintf(&new_blk_device, "/dev/block/%s", ent->d_name) < 0) {
-                ERROR("Could not allocate block device string\n");
+                LERROR << "Could not allocate block device string";
                 goto out;
             }
 
-            INFO("resolved label %s to %s\n", rec->blk_device, new_blk_device);
+            LINFO << "resolved label " << rec->blk_device << " to "
+                  << new_blk_device;
 
             free(rec->blk_device);
             rec->blk_device = new_blk_device;
@@ -559,13 +635,13 @@
         if (umount(rec->mount_point) == 0) {
             return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
         } else {
-            WARNING("Could not umount %s (%s) - allow continue unencrypted\n",
-                    rec->mount_point, strerror(errno));
+            PWARNING << "Could not umount " << rec->mount_point
+                     << " - allow continue unencrypted";
             return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
         }
     } else if (rec->fs_mgr_flags & (MF_FILEENCRYPTION | MF_FORCEFDEORFBE)) {
-    // Deal with file level encryption
-        INFO("%s is file encrypted\n", rec->mount_point);
+        // 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)) {
         return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
@@ -574,6 +650,36 @@
     }
 }
 
+// TODO: add ueventd notifiers if they don't exist.
+// This is just doing a wait_for_device for maximum of 1s
+int fs_mgr_test_access(const char *device) {
+    int tries = 25;
+    while (tries--) {
+        if (!access(device, F_OK) || errno != ENOENT) {
+            return 0;
+        }
+        usleep(40 * 1000);
+    }
+    return -1;
+}
+
+bool is_device_secure() {
+    int ret = -1;
+    char value[PROP_VALUE_MAX];
+    ret = __system_property_get("ro.secure", value);
+    if (ret == 0) {
+#ifdef ALLOW_SKIP_SECURE_CHECK
+        // Allow eng builds to skip this check if the property
+        // is not readable (happens during early mount)
+        return false;
+#else
+        // If error and not an 'eng' build, we want to fail secure.
+        return true;
+#endif
+    }
+    return strcmp(value, "0") ? true : false;
+}
+
 /* When multiple fstab records share the same mount_point, it will
  * try to mount each one in turn, and ignore any duplicates after a
  * first successful mount.
@@ -587,11 +693,17 @@
     int mret = -1;
     int mount_errno = 0;
     int attempted_idx = -1;
+    int avb_ret = FS_MGR_SETUP_AVB_FAIL;
 
     if (!fstab) {
         return -1;
     }
 
+    if (fs_mgr_is_avb_used() &&
+        (avb_ret = fs_mgr_load_vbmeta_images(fstab)) == FS_MGR_SETUP_AVB_FAIL) {
+        return -1;
+    }
+
     for (i = 0; i < fstab->num_entries; i++) {
         /* Don't mount entries that are managed by vold or not for the mount mode*/
         if ((fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) ||
@@ -621,7 +733,7 @@
             !strcmp(fstab->recs[i].fs_type, "ext4")) {
             int tret = translate_ext_labels(&fstab->recs[i]);
             if (tret < 0) {
-                ERROR("Could not translate label to block device\n");
+                LERROR << "Could not translate label to block device";
                 continue;
             }
         }
@@ -630,15 +742,31 @@
             wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
         }
 
-        if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
-            int rc = fs_mgr_setup_verity(&fstab->recs[i]);
+        if (fs_mgr_is_avb_used() && (fstab->recs[i].fs_mgr_flags & MF_AVB)) {
+            /* If HASHTREE_DISABLED is set (cf. 'adb disable-verity'), we
+             * should set up the device without using dm-verity.
+             * The actual mounting still take place in the following
+             * mount_with_alternatives().
+             */
+            if (avb_ret == FS_MGR_SETUP_AVB_HASHTREE_DISABLED) {
+                LINFO << "AVB HASHTREE disabled";
+            } else if (fs_mgr_setup_avb(&fstab->recs[i]) !=
+                       FS_MGR_SETUP_AVB_SUCCESS) {
+                LERROR << "Failed to set up AVB on partition: "
+                       << fstab->recs[i].mount_point << ", skipping!";
+                /* Skips mounting the device. */
+                continue;
+            }
+        } 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) {
-                INFO("Verity disabled");
+                LINFO << "Verity disabled";
             } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
-                ERROR("Could not set up verified partition, skipping!\n");
+                LERROR << "Could not set up verified partition, skipping!";
                 continue;
             }
         }
+
         int last_idx_inspected;
         int top_idx = i;
 
@@ -658,7 +786,7 @@
             if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
                 if (encryptable != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
                     // Log and continue
-                    ERROR("Only one encryptable/encrypted partition supported\n");
+                    LERROR << "Only one encryptable/encrypted partition supported";
                 }
                 encryptable = status;
             }
@@ -676,19 +804,21 @@
              * at two different lines in the fstab.  Use the top one for formatting
              * as that is the preferred one.
              */
-            ERROR("%s(): %s is wiped and %s %s is formattable. Format it.\n", __func__,
-                  fstab->recs[top_idx].blk_device, fstab->recs[top_idx].mount_point,
-                  fstab->recs[top_idx].fs_type);
+            LERROR << __FUNCTION__ << "(): " << fstab->recs[top_idx].blk_device
+                   << " is wiped and " << fstab->recs[top_idx].mount_point
+                   << " " << fstab->recs[top_idx].fs_type
+                   << " is formattable. Format it.";
             if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
                 strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
                 int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY);
                 if (fd >= 0) {
-                    INFO("%s(): also wipe %s\n", __func__, fstab->recs[top_idx].key_loc);
+                    LINFO << __FUNCTION__ << "(): also wipe "
+                          << fstab->recs[top_idx].key_loc;
                     wipe_block_device(fd, get_file_size(fd));
                     close(fd);
                 } else {
-                    ERROR("%s(): %s wouldn't open (%s)\n", __func__,
-                          fstab->recs[top_idx].key_loc, strerror(errno));
+                    PERROR << __FUNCTION__ << "(): "
+                           << fstab->recs[top_idx].key_loc << " wouldn't open";
                 }
             } else if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
                 !strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
@@ -699,7 +829,8 @@
                 i = top_idx - 1;
                 continue;
             } else {
-                ERROR("%s(): Format failed. Suggest recovery...\n", __func__);
+                LERROR << __FUNCTION__ << "(): Format failed. "
+                       << "Suggest recovery...";
                 encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY;
                 continue;
             }
@@ -707,18 +838,22 @@
         if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
             fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) {
             if (wiped) {
-                ERROR("%s(): %s is wiped and %s %s is encryptable. Suggest recovery...\n", __func__,
-                      fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
-                      fstab->recs[attempted_idx].fs_type);
+                LERROR << __FUNCTION__ << "(): "
+                       << fstab->recs[attempted_idx].blk_device
+                       << " is wiped and "
+                       << fstab->recs[attempted_idx].mount_point << " "
+                       << fstab->recs[attempted_idx].fs_type
+                       << " is encryptable. Suggest recovery...";
                 encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY;
                 continue;
             } else {
                 /* Need to mount a tmpfs at this mountpoint for now, and set
                  * properties that vold will query later for decrypting
                  */
-                ERROR("%s(): possibly an encryptable blkdev %s for mount %s type %s )\n", __func__,
-                      fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
-                      fstab->recs[attempted_idx].fs_type);
+                LERROR << __FUNCTION__ << "(): possibly an encryptable blkdev "
+                       << fstab->recs[attempted_idx].blk_device
+                       << " for mount " << fstab->recs[attempted_idx].mount_point
+                       << " type " << fstab->recs[attempted_idx].fs_type;
                 if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) {
                     ++error_count;
                     continue;
@@ -727,21 +862,25 @@
             encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
         } else {
             if (fs_mgr_is_nofail(&fstab->recs[attempted_idx])) {
-                ERROR("Ignoring failure to mount an un-encryptable or wiped partition on"
-                       "%s at %s options: %s error: %s\n",
-                       fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
-                       fstab->recs[attempted_idx].fs_options, strerror(mount_errno));
+                PERROR << "Ignoring failure to mount an un-encryptable or wiped partition on"
+                       << fstab->recs[attempted_idx].blk_device << " at "
+                       << fstab->recs[attempted_idx].mount_point << " options: "
+                       << fstab->recs[attempted_idx].fs_options;
             } else {
-                ERROR("Failed to mount an un-encryptable or wiped partition on"
-                       "%s at %s options: %s error: %s\n",
-                       fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
-                       fstab->recs[attempted_idx].fs_options, strerror(mount_errno));
+                PERROR << "Failed to mount an un-encryptable or wiped partition on"
+                       << fstab->recs[attempted_idx].blk_device << " at "
+                       << fstab->recs[attempted_idx].mount_point << " options: "
+                       << fstab->recs[attempted_idx].fs_options;
                 ++error_count;
             }
             continue;
         }
     }
 
+    if (fs_mgr_is_avb_used()) {
+        fs_mgr_unload_vbmeta_images();
+    }
+
     if (error_count) {
         return -1;
     } else {
@@ -749,6 +888,24 @@
     }
 }
 
+/* wrapper to __mount() and expects a fully prepared fstab_rec,
+ * unlike fs_mgr_do_mount which does more things with avb / verity
+ * etc.
+ */
+int fs_mgr_do_mount_one(struct fstab_rec *rec)
+{
+    if (!rec) {
+        return FS_MGR_DOMNT_FAILED;
+    }
+
+    int ret = __mount(rec->blk_device, rec->mount_point, rec);
+    if (ret) {
+      ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
+    }
+
+    return ret;
+}
+
 /* If tmp_mount_point is non-null, mount the filesystem there.  This is for the
  * tmp mount we do to check the user password
  * If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
@@ -762,11 +919,17 @@
     int mount_errors = 0;
     int first_mount_errno = 0;
     char *m;
+    int avb_ret = FS_MGR_SETUP_AVB_FAIL;
 
     if (!fstab) {
         return ret;
     }
 
+    if (fs_mgr_is_avb_used() &&
+        (avb_ret = fs_mgr_load_vbmeta_images(fstab)) == FS_MGR_SETUP_AVB_FAIL) {
+        return ret;
+    }
+
     for (i = 0; i < fstab->num_entries; i++) {
         if (!fs_match(fstab->recs[i].mount_point, n_name)) {
             continue;
@@ -777,8 +940,8 @@
         if (!strcmp(fstab->recs[i].fs_type, "swap") ||
             !strcmp(fstab->recs[i].fs_type, "emmc") ||
             !strcmp(fstab->recs[i].fs_type, "mtd")) {
-            ERROR("Cannot mount filesystem of type %s on %s\n",
-                  fstab->recs[i].fs_type, n_blk_device);
+            LERROR << "Cannot mount filesystem of type "
+                   << fstab->recs[i].fs_type << " on " << n_blk_device;
             goto out;
         }
 
@@ -787,7 +950,10 @@
             wait_for_file(n_blk_device, WAIT_TIMEOUT);
         }
 
-        if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
+        int force_check = do_quota(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+                                   &fstab->recs[i]);
+
+        if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
             check_fs(n_blk_device, fstab->recs[i].fs_type,
                      fstab->recs[i].mount_point);
         }
@@ -796,12 +962,27 @@
             do_reserved_size(n_blk_device, fstab->recs[i].fs_type, &fstab->recs[i]);
         }
 
-        if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
-            int rc = fs_mgr_setup_verity(&fstab->recs[i]);
+        if (fs_mgr_is_avb_used() && (fstab->recs[i].fs_mgr_flags & MF_AVB)) {
+            /* If HASHTREE_DISABLED is set (cf. 'adb disable-verity'), we
+             * should set up the device without using dm-verity.
+             * The actual mounting still take place in the following
+             * mount_with_alternatives().
+             */
+            if (avb_ret == FS_MGR_SETUP_AVB_HASHTREE_DISABLED) {
+                LINFO << "AVB HASHTREE disabled";
+            } else if (fs_mgr_setup_avb(&fstab->recs[i]) !=
+                       FS_MGR_SETUP_AVB_SUCCESS) {
+                LERROR << "Failed to set up AVB on partition: "
+                       << fstab->recs[i].mount_point << ", skipping!";
+                /* Skips mounting the device. */
+                continue;
+            }
+        } 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) {
-                INFO("Verity disabled");
+                LINFO << "Verity disabled";
             } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
-                ERROR("Could not set up verified partition, skipping!\n");
+                LERROR << "Could not set up verified partition, skipping!";
                 continue;
             }
         }
@@ -822,8 +1003,8 @@
         }
     }
     if (mount_errors) {
-        ERROR("Cannot mount filesystem on %s at %s. error: %s\n",
-            n_blk_device, m, strerror(first_mount_errno));
+        PERROR << "Cannot mount filesystem on " << n_blk_device
+               << " at " << m;
         if (first_mount_errno == EBUSY) {
             ret = FS_MGR_DOMNT_BUSY;
         } else {
@@ -831,10 +1012,14 @@
         }
     } else {
         /* We didn't find a match, say so and return an error */
-        ERROR("Cannot find mount point %s in fstab\n", fstab->recs[i].mount_point);
+        LERROR << "Cannot find mount point " << fstab->recs[i].mount_point
+               << " in fstab";
     }
 
 out:
+    if (fs_mgr_is_avb_used()) {
+        fs_mgr_unload_vbmeta_images();
+    }
     return ret;
 }
 
@@ -842,14 +1027,14 @@
  * mount a tmpfs filesystem at the given point.
  * return 0 on success, non-zero on failure.
  */
-int fs_mgr_do_tmpfs_mount(char *n_name)
+int fs_mgr_do_tmpfs_mount(const char *n_name)
 {
     int ret;
 
     ret = mount("tmpfs", n_name, "tmpfs",
                 MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS);
     if (ret < 0) {
-        ERROR("Cannot mount tmpfs filesystem at %s\n", n_name);
+        LERROR << "Cannot mount tmpfs filesystem at " << n_name;
         return -1;
     }
 
@@ -868,7 +1053,8 @@
 
     while (fstab->recs[i].blk_device) {
         if (umount(fstab->recs[i].mount_point)) {
-            ERROR("Cannot unmount filesystem at %s\n", fstab->recs[i].mount_point);
+            LERROR << "Cannot unmount filesystem at "
+                   << fstab->recs[i].mount_point;
             ret = -1;
         }
         i++;
@@ -887,9 +1073,9 @@
     int err = 0;
     int ret = 0;
     int status;
-    char *mkswap_argv[2] = {
+    const char *mkswap_argv[2] = {
         MKSWAP_BIN,
-        NULL
+        nullptr
     };
 
     if (!fstab) {
@@ -914,7 +1100,8 @@
             if (fstab->recs[i].max_comp_streams >= 0) {
                zram_mcs_fp = fopen(ZRAM_CONF_MCS, "r+");
               if (zram_mcs_fp == NULL) {
-                ERROR("Unable to open zram conf comp device %s\n", ZRAM_CONF_MCS);
+                LERROR << "Unable to open zram conf comp device "
+                       << ZRAM_CONF_MCS;
                 ret = -1;
                 continue;
               }
@@ -924,7 +1111,7 @@
 
             zram_fp = fopen(ZRAM_CONF_DEV, "r+");
             if (zram_fp == NULL) {
-                ERROR("Unable to open zram conf device %s\n", ZRAM_CONF_DEV);
+                LERROR << "Unable to open zram conf device " << ZRAM_CONF_DEV;
                 ret = -1;
                 continue;
             }
@@ -938,11 +1125,12 @@
 
         /* Initialize the swap area */
         mkswap_argv[1] = fstab->recs[i].blk_device;
-        err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), mkswap_argv,
+        err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv),
+                                      const_cast<char **>(mkswap_argv),
                                       &status, true, LOG_KLOG, false, NULL,
                                       NULL, 0);
         if (err) {
-            ERROR("mkswap failed for %s\n", fstab->recs[i].blk_device);
+            LERROR << "mkswap failed for " << fstab->recs[i].blk_device;
             ret = -1;
             continue;
         }
@@ -958,7 +1146,7 @@
         }
         err = swapon(fstab->recs[i].blk_device, flags);
         if (err) {
-            ERROR("swapon failed for %s\n", fstab->recs[i].blk_device);
+            LERROR << "swapon failed for " << fstab->recs[i].blk_device;
             ret = -1;
         }
     }
@@ -1009,22 +1197,3 @@
 
     return 0;
 }
-
-int fs_mgr_early_setup_verity(struct fstab_rec *fstab_rec)
-{
-    if ((fstab_rec->fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
-        int rc = fs_mgr_setup_verity(fstab_rec);
-        if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
-            INFO("Verity disabled");
-            return FS_MGR_EARLY_SETUP_VERITY_NO_VERITY;
-        } else if (rc == FS_MGR_SETUP_VERITY_SUCCESS) {
-            return FS_MGR_EARLY_SETUP_VERITY_SUCCESS;
-        } else {
-            return FS_MGR_EARLY_SETUP_VERITY_FAIL;
-        }
-    } else if (device_is_secure()) {
-        ERROR("Verity must be enabled for early mounted partitions on secured devices.\n");
-        return FS_MGR_EARLY_SETUP_VERITY_FAIL;
-    }
-    return FS_MGR_EARLY_SETUP_VERITY_NO_VERITY;
-}
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
new file mode 100644
index 0000000..2cb7e34
--- /dev/null
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -0,0 +1,572 @@
+/*
+ * 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 <libgen.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.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_avb_ops.h"
+#include "fs_mgr_priv.h"
+#include "fs_mgr_priv_avb.h"
+#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
+
+AvbSlotVerifyData* fs_mgr_avb_verify_data = nullptr;
+AvbOps* fs_mgr_avb_ops = nullptr;
+
+enum HashAlgorithm {
+    kInvalid = 0,
+    kSHA256 = 1,
+    kSHA512 = 2,
+};
+
+struct androidboot_vbmeta {
+    HashAlgorithm hash_alg;
+    uint8_t digest[SHA512_DIGEST_LENGTH];
+    size_t vbmeta_size;
+    bool allow_verification_error;
+};
+
+androidboot_vbmeta fs_mgr_vbmeta_prop;
+
+static inline bool nibble_value(const char& c, uint8_t* value) {
+    FS_MGR_CHECK(value != nullptr);
+
+    switch (c) {
+        case '0' ... '9':
+            *value = c - '0';
+            break;
+        case 'a' ... 'f':
+            *value = c - 'a' + 10;
+            break;
+        case 'A' ... 'F':
+            *value = c - 'A' + 10;
+            break;
+        default:
+            return false;
+    }
+
+    return true;
+}
+
+static bool hex_to_bytes(uint8_t* bytes, size_t bytes_len, const std::string& hex) {
+    FS_MGR_CHECK(bytes != nullptr);
+
+    if (hex.size() % 2 != 0) {
+        return false;
+    }
+    if (hex.size() / 2 > bytes_len) {
+        return false;
+    }
+    for (size_t i = 0, j = 0, n = hex.size(); i < n; i += 2, ++j) {
+        uint8_t high;
+        if (!nibble_value(hex[i], &high)) {
+            return false;
+        }
+        uint8_t low;
+        if (!nibble_value(hex[i + 1], &low)) {
+            return false;
+        }
+        bytes[j] = (high << 4) | low;
+    }
+    return true;
+}
+
+static std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
+    FS_MGR_CHECK(bytes != nullptr);
+
+    static const char* hex_digits = "0123456789abcdef";
+    std::string hex;
+
+    for (size_t i = 0; i < bytes_len; i++) {
+        hex.push_back(hex_digits[(bytes[i] & 0xF0) >> 4]);
+        hex.push_back(hex_digits[bytes[i] & 0x0F]);
+    }
+    return hex;
+}
+
+static bool load_vbmeta_prop(androidboot_vbmeta* vbmeta_prop) {
+    FS_MGR_CHECK(vbmeta_prop != nullptr);
+
+    std::string cmdline;
+    android::base::ReadFileToString("/proc/cmdline", &cmdline);
+
+    std::string hash_alg;
+    std::string digest;
+
+    for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
+        std::vector<std::string> pieces = android::base::Split(entry, "=");
+        const std::string& key = pieces[0];
+        const std::string& value = pieces[1];
+
+        if (key == "androidboot.vbmeta.device_state") {
+            vbmeta_prop->allow_verification_error = (value == "unlocked");
+        } else if (key == "androidboot.vbmeta.hash_alg") {
+            hash_alg = value;
+        } else if (key == "androidboot.vbmeta.size") {
+            if (!android::base::ParseUint(value.c_str(), &vbmeta_prop->vbmeta_size)) {
+                return false;
+            }
+        } else if (key == "androidboot.vbmeta.digest") {
+            digest = value;
+        }
+    }
+
+    // Reads hash algorithm.
+    size_t expected_digest_size = 0;
+    if (hash_alg == "sha256") {
+        expected_digest_size = SHA256_DIGEST_LENGTH * 2;
+        vbmeta_prop->hash_alg = kSHA256;
+    } else if (hash_alg == "sha512") {
+        expected_digest_size = SHA512_DIGEST_LENGTH * 2;
+        vbmeta_prop->hash_alg = kSHA512;
+    } else {
+        LERROR << "Unknown hash algorithm: " << hash_alg.c_str();
+        return false;
+    }
+
+    // Reads digest.
+    if (digest.size() != expected_digest_size) {
+        LERROR << "Unexpected digest size: " << digest.size()
+               << " (expected: " << expected_digest_size << ")";
+        return false;
+    }
+
+    if (!hex_to_bytes(vbmeta_prop->digest, sizeof(vbmeta_prop->digest), digest)) {
+        LERROR << "Hash digest contains non-hexidecimal character: " << digest.c_str();
+        return false;
+    }
+
+    return true;
+}
+
+template <typename Hasher>
+static std::pair<size_t, bool> verify_vbmeta_digest(const AvbSlotVerifyData& verify_data,
+                                                    const androidboot_vbmeta& vbmeta_prop) {
+    size_t total_size = 0;
+    Hasher hasher;
+    for (size_t n = 0; n < verify_data.num_vbmeta_images; n++) {
+        hasher.update(verify_data.vbmeta_images[n].vbmeta_data,
+                      verify_data.vbmeta_images[n].vbmeta_size);
+        total_size += verify_data.vbmeta_images[n].vbmeta_size;
+    }
+
+    bool matched = (memcmp(hasher.finalize(), vbmeta_prop.digest, Hasher::DIGEST_SIZE) == 0);
+
+    return std::make_pair(total_size, matched);
+}
+
+static bool verify_vbmeta_images(const AvbSlotVerifyData& verify_data,
+                                 const androidboot_vbmeta& vbmeta_prop) {
+    if (verify_data.num_vbmeta_images == 0) {
+        LERROR << "No vbmeta images";
+        return false;
+    }
+
+    size_t total_size = 0;
+    bool digest_matched = false;
+
+    if (vbmeta_prop.hash_alg == kSHA256) {
+        std::tie(total_size, digest_matched) =
+            verify_vbmeta_digest<SHA256Hasher>(verify_data, vbmeta_prop);
+    } else if (vbmeta_prop.hash_alg == kSHA512) {
+        std::tie(total_size, digest_matched) =
+            verify_vbmeta_digest<SHA512Hasher>(verify_data, vbmeta_prop);
+    }
+
+    if (total_size != vbmeta_prop.vbmeta_size) {
+        LERROR << "total vbmeta size mismatch: " << total_size
+               << " (expected: " << vbmeta_prop.vbmeta_size << ")";
+        return false;
+    }
+
+    if (!digest_matched) {
+        LERROR << "vbmeta digest mismatch";
+        return false;
+    }
+
+    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) {
+    fs_mgr_verity_ioctl_init(io, dm_device_name, DM_STATUS_TABLE_FLAG);
+
+    // The buffer consists of [dm_ioctl][dm_target_spec][verity_params].
+    char* buffer = (char*)io;
+
+    // Builds the dm_target_spec arguments.
+    struct dm_target_spec* dm_target = (struct dm_target_spec*)&buffer[sizeof(struct dm_ioctl)];
+    io->target_count = 1;
+    dm_target->status = 0;
+    dm_target->sector_start = 0;
+    dm_target->length = hashtree_desc.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);
+    }
+
+    if (res < 0 || (size_t)res >= bufsize) {
+        LERROR << "Error building verity table; insufficient buffer size?";
+        return false;
+    }
+
+    LINFO << "Loading verity table: '" << verity_params << "'";
+
+    // Sets ext target boundary.
+    verity_params += strlen(verity_params) + 1;
+    verity_params = (char*)(((unsigned long)verity_params + 7) & ~7);
+    dm_target->next = verity_params - buffer;
+
+    // Sends the ioctl to load the verity table.
+    if (ioctl(fd, DM_TABLE_LOAD, io)) {
+        PERROR << "Error loading verity table";
+        return false;
+    }
+
+    return true;
+}
+
+static bool hashtree_dm_verity_setup(struct fstab_rec* fstab_entry,
+                                     const AvbHashtreeDescriptor& hashtree_desc,
+                                     const std::string& salt, const std::string& root_digest) {
+    // Gets the device mapper fd.
+    android::base::unique_fd fd(open("/dev/device-mapper", O_RDWR));
+    if (fd < 0) {
+        PERROR << "Error opening device mapper";
+        return false;
+    }
+
+    // Creates the device.
+    alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
+    struct dm_ioctl* io = (struct dm_ioctl*)buffer;
+    const std::string mount_point(basename(fstab_entry->mount_point));
+    if (!fs_mgr_create_verity_device(io, mount_point, fd)) {
+        LERROR << "Couldn't create verity device!";
+        return false;
+    }
+
+    // Gets the name of the device file.
+    std::string verity_blk_name;
+    if (!fs_mgr_get_verity_device_name(io, mount_point, fd, &verity_blk_name)) {
+        LERROR << "Couldn't get verity device number!";
+        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)) {
+        LERROR << "Couldn't load verity table!";
+        return false;
+    }
+
+    // Activates the device.
+    if (!fs_mgr_resume_verity_table(io, mount_point, fd)) {
+        return false;
+    }
+
+    // Marks the underlying block device as read-only.
+    fs_mgr_set_blk_ro(fstab_entry->blk_device);
+
+    // TODO(bowgotsai): support verified all partition at boot.
+    // Updates fstab_rec->blk_device to verity device name.
+    free(fstab_entry->blk_device);
+    fstab_entry->blk_device = strdup(verity_blk_name.c_str());
+
+    // Makes sure we've set everything up properly.
+    if (fs_mgr_test_access(verity_blk_name.c_str()) < 0) {
+        return false;
+    }
+
+    return true;
+}
+
+static bool get_hashtree_descriptor(const std::string& partition_name,
+                                    const AvbSlotVerifyData& verify_data,
+                                    AvbHashtreeDescriptor* out_hashtree_desc, std::string* out_salt,
+                                    std::string* out_digest) {
+    bool found = false;
+    const uint8_t* desc_partition_name;
+
+    for (size_t i = 0; i < verify_data.num_vbmeta_images && !found; i++) {
+        // Get descriptors from vbmeta_images[i].
+        size_t num_descriptors;
+        std::unique_ptr<const AvbDescriptor* [], decltype(&avb_free)> descriptors(
+            avb_descriptor_get_all(verify_data.vbmeta_images[i].vbmeta_data,
+                                   verify_data.vbmeta_images[i].vbmeta_size, &num_descriptors),
+            avb_free);
+
+        if (!descriptors || num_descriptors < 1) {
+            continue;
+        }
+
+        // Ensures that hashtree descriptor is either in /vbmeta or in
+        // the same partition for verity setup.
+        std::string vbmeta_partition_name(verify_data.vbmeta_images[i].partition_name);
+        if (vbmeta_partition_name != "vbmeta" && vbmeta_partition_name != partition_name) {
+            LWARNING << "Skip vbmeta image at " << verify_data.vbmeta_images[i].partition_name
+                     << " for partition: " << partition_name.c_str();
+            continue;
+        }
+
+        for (size_t j = 0; j < num_descriptors && !found; j++) {
+            AvbDescriptor desc;
+            if (!avb_descriptor_validate_and_byteswap(descriptors[j], &desc)) {
+                LWARNING << "Descriptor[" << j << "] is invalid";
+                continue;
+            }
+            if (desc.tag == AVB_DESCRIPTOR_TAG_HASHTREE) {
+                desc_partition_name =
+                    (const uint8_t*)descriptors[j] + sizeof(AvbHashtreeDescriptor);
+                if (!avb_hashtree_descriptor_validate_and_byteswap(
+                        (AvbHashtreeDescriptor*)descriptors[j], out_hashtree_desc)) {
+                    continue;
+                }
+                if (out_hashtree_desc->partition_name_len != partition_name.length()) {
+                    continue;
+                }
+                // Notes that desc_partition_name is not NUL-terminated.
+                std::string hashtree_partition_name((const char*)desc_partition_name,
+                                                    out_hashtree_desc->partition_name_len);
+                if (hashtree_partition_name == partition_name) {
+                    found = true;
+                }
+            }
+        }
+    }
+
+    if (!found) {
+        LERROR << "Partition descriptor not found: " << partition_name.c_str();
+        return false;
+    }
+
+    const uint8_t* desc_salt = desc_partition_name + out_hashtree_desc->partition_name_len;
+    *out_salt = bytes_to_hex(desc_salt, out_hashtree_desc->salt_len);
+
+    const uint8_t* desc_digest = desc_salt + out_hashtree_desc->salt_len;
+    *out_digest = bytes_to_hex(desc_digest, out_hashtree_desc->root_digest_len);
+
+    return true;
+}
+
+static bool init_is_avb_used() {
+    // When AVB is used, boot loader should set androidboot.vbmeta.{hash_alg,
+    // size, digest} in kernel cmdline or in device tree. They will then be
+    // imported by init process to system properties: ro.boot.vbmeta.{hash_alg, size, digest}.
+    //
+    // In case of early mount, init properties are not initialized, so we also
+    // ensure we look into kernel command line and device tree if the property is
+    // not found
+    //
+    // Checks hash_alg as an indicator for whether AVB is used.
+    // We don't have to parse and check all of them here. The check will
+    // be done in fs_mgr_load_vbmeta_images() and FS_MGR_SETUP_AVB_FAIL will
+    // be returned when there is an error.
+
+    std::string hash_alg;
+    if (fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg) == 0) {
+        if (hash_alg == "sha256" || hash_alg == "sha512") {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+bool fs_mgr_is_avb_used() {
+    static bool result = init_is_avb_used();
+    return result;
+}
+
+int fs_mgr_load_vbmeta_images(struct fstab* fstab) {
+    FS_MGR_CHECK(fstab != nullptr);
+
+    // Gets the expected hash value of vbmeta images from
+    // kernel cmdline.
+    if (!load_vbmeta_prop(&fs_mgr_vbmeta_prop)) {
+        return FS_MGR_SETUP_AVB_FAIL;
+    }
+
+    fs_mgr_avb_ops = fs_mgr_dummy_avb_ops_new(fstab);
+    if (fs_mgr_avb_ops == nullptr) {
+        LERROR << "Failed to allocate dummy avb_ops";
+        return FS_MGR_SETUP_AVB_FAIL;
+    }
+
+    // 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 fs_mgr_avb_verify_data, which is not required as
+    // fs_mgr only deals with HASHTREE partitions.
+    const char *requested_partitions[] = {nullptr};
+    std::string ab_suffix;
+    fs_mgr_get_boot_config("slot_suffix", &ab_suffix);
+    AvbSlotVerifyResult verify_result =
+        avb_slot_verify(fs_mgr_avb_ops, requested_partitions, ab_suffix.c_str(),
+                        fs_mgr_vbmeta_prop.allow_verification_error, &fs_mgr_avb_verify_data);
+
+    // Only allow two verify results:
+    //   - AVB_SLOT_VERIFY_RESULT_OK.
+    //   - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (for UNLOCKED state).
+    if (verify_result == AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION) {
+        if (!fs_mgr_vbmeta_prop.allow_verification_error) {
+            LERROR << "ERROR_VERIFICATION isn't allowed";
+            goto fail;
+        }
+    } else if (verify_result != AVB_SLOT_VERIFY_RESULT_OK) {
+        LERROR << "avb_slot_verify failed, result: " << verify_result;
+        goto fail;
+    }
+
+    // Verifies vbmeta images against the digest passed from bootloader.
+    if (!verify_vbmeta_images(*fs_mgr_avb_verify_data, fs_mgr_vbmeta_prop)) {
+        LERROR << "verify_vbmeta_images failed";
+        goto fail;
+    } else {
+        // Checks whether FLAGS_HASHTREE_DISABLED is set.
+        AvbVBMetaImageHeader vbmeta_header;
+        avb_vbmeta_image_header_to_host_byte_order(
+            (AvbVBMetaImageHeader*)fs_mgr_avb_verify_data->vbmeta_images[0].vbmeta_data,
+            &vbmeta_header);
+
+        bool hashtree_disabled =
+            ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+        if (hashtree_disabled) {
+            return FS_MGR_SETUP_AVB_HASHTREE_DISABLED;
+        }
+    }
+
+    if (verify_result == AVB_SLOT_VERIFY_RESULT_OK) {
+        return FS_MGR_SETUP_AVB_SUCCESS;
+    }
+
+fail:
+    fs_mgr_unload_vbmeta_images();
+    return FS_MGR_SETUP_AVB_FAIL;
+}
+
+void fs_mgr_unload_vbmeta_images() {
+    if (fs_mgr_avb_verify_data != nullptr) {
+        avb_slot_verify_data_free(fs_mgr_avb_verify_data);
+    }
+
+    if (fs_mgr_avb_ops != nullptr) {
+        fs_mgr_dummy_avb_ops_free(fs_mgr_avb_ops);
+    }
+}
+
+int fs_mgr_setup_avb(struct fstab_rec* fstab_entry) {
+    if (!fstab_entry || !fs_mgr_avb_verify_data || fs_mgr_avb_verify_data->num_vbmeta_images < 1) {
+        return FS_MGR_SETUP_AVB_FAIL;
+    }
+
+    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 FS_MGR_SETUP_AVB_FAIL;
+    }
+
+    AvbHashtreeDescriptor hashtree_descriptor;
+    std::string salt;
+    std::string root_digest;
+    if (!get_hashtree_descriptor(partition_name, *fs_mgr_avb_verify_data, &hashtree_descriptor,
+                                 &salt, &root_digest)) {
+        return FS_MGR_SETUP_AVB_FAIL;
+    }
+
+    // Converts HASHTREE descriptor to verity_table_params.
+    if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt, root_digest)) {
+        return FS_MGR_SETUP_AVB_FAIL;
+    }
+
+    return FS_MGR_SETUP_AVB_SUCCESS;
+}
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
new file mode 100644
index 0000000..dd8c7e7
--- /dev/null
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <string>
+
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <libavb/libavb.h>
+#include <utils/Compat.h>
+
+#include "fs_mgr.h"
+#include "fs_mgr_avb_ops.h"
+#include "fs_mgr_priv.h"
+
+static struct fstab* fs_mgr_fstab = nullptr;
+
+static AvbIOResult read_from_partition(AvbOps* ops ATTRIBUTE_UNUSED, const char* partition,
+                                       int64_t offset, size_t num_bytes, void* buffer,
+                                       size_t* out_num_read) {
+    // The input |partition| name is with ab_suffix, e.g. system_a.
+    // Slot suffix (e.g. _a) will be appended to the device file path
+    // for partitions having 'slotselect' optin in fstab file, but it
+    // won't be appended to the mount point.
+    //
+    // In AVB, we can assume that there's an entry for the /misc mount
+    // point and use that to get the device file for the misc partition.
+    // From there we'll assume that a by-name scheme is used
+    // so we can just replace the trailing "misc" by the given
+    // |partition|, e.g.
+    //
+    //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+    //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a
+
+    struct fstab_rec* fstab_entry = fs_mgr_get_entry_for_mount_point(fs_mgr_fstab, "/misc");
+
+    if (fstab_entry == nullptr) {
+        LERROR << "/misc mount point not found in fstab";
+        return AVB_IO_RESULT_ERROR_IO;
+    }
+
+    std::string partition_name(partition);
+    std::string path(fstab_entry->blk_device);
+    // Replaces the last field of device file if it's not misc.
+    if (!android::base::StartsWith(partition_name, "misc")) {
+        size_t end_slash = path.find_last_of("/");
+        std::string by_name_prefix(path.substr(0, end_slash + 1));
+        path = by_name_prefix + partition_name;
+    }
+
+    // Ensures the device path (a symlink created by init) is ready to
+    // access. fs_mgr_test_access() will test a few iterations if the
+    // path doesn't exist yet.
+    if (fs_mgr_test_access(path.c_str()) < 0) {
+        return AVB_IO_RESULT_ERROR_IO;
+    }
+
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+
+    if (fd < 0) {
+        PERROR << "Failed to open " << path.c_str();
+        return AVB_IO_RESULT_ERROR_IO;
+    }
+
+    // If offset is negative, interprets its absolute value as the
+    //  number of bytes from the end of the partition.
+    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";
+            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";
+            return AVB_IO_RESULT_ERROR_IO;
+        }
+    }
+
+    // On Linux, we never get partial reads from block devices (except
+    // for EOF).
+    ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, buffer, num_bytes, offset));
+
+    if (num_read < 0 || (size_t)num_read != num_bytes) {
+        PERROR << "Failed to read " << num_bytes << " bytes from " << path.c_str() << " offset "
+               << offset;
+        return AVB_IO_RESULT_ERROR_IO;
+    }
+
+    if (out_num_read != nullptr) {
+        *out_num_read = num_read;
+    }
+
+    return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult dummy_read_rollback_index(AvbOps* ops ATTRIBUTE_UNUSED,
+                                             size_t rollback_index_location ATTRIBUTE_UNUSED,
+                                             uint64_t* out_rollback_index) {
+    // rollback_index has been checked in bootloader phase.
+    // In user-space, returns the smallest value 0 to pass the check.
+    *out_rollback_index = 0;
+    return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult dummy_validate_vbmeta_public_key(
+    AvbOps* ops ATTRIBUTE_UNUSED, const uint8_t* public_key_data ATTRIBUTE_UNUSED,
+    size_t public_key_length ATTRIBUTE_UNUSED, const uint8_t* public_key_metadata ATTRIBUTE_UNUSED,
+    size_t public_key_metadata_length ATTRIBUTE_UNUSED, bool* out_is_trusted) {
+    // vbmeta public key has been checked in bootloader phase.
+    // In user-space, returns true to pass the check.
+    //
+    // Addtionally, user-space should check
+    // androidboot.vbmeta.{hash_alg, size, digest} against the digest
+    // of all vbmeta images after invoking avb_slot_verify().
+
+    *out_is_trusted = true;
+    return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult dummy_read_is_device_unlocked(AvbOps* ops ATTRIBUTE_UNUSED,
+                                                 bool* out_is_unlocked) {
+    // The function is for bootloader to update the value into
+    // androidboot.vbmeta.device_state in kernel cmdline.
+    // In user-space, returns true as we don't need to update it anymore.
+    *out_is_unlocked = true;
+    return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult dummy_get_unique_guid_for_partition(AvbOps* ops ATTRIBUTE_UNUSED,
+                                                       const char* partition ATTRIBUTE_UNUSED,
+                                                       char* guid_buf, size_t guid_buf_size) {
+    // The function is for bootloader to set the correct UUID
+    // for a given partition in kernel cmdline.
+    // In user-space, returns a faking one as we don't need to update
+    // it anymore.
+    snprintf(guid_buf, guid_buf_size, "1234-fake-guid-for:%s", partition);
+    return AVB_IO_RESULT_OK;
+}
+
+AvbOps* fs_mgr_dummy_avb_ops_new(struct fstab* fstab) {
+    AvbOps* ops;
+
+    // Assigns the fstab to the static variable for later use.
+    fs_mgr_fstab = fstab;
+
+    ops = (AvbOps*)calloc(1, sizeof(AvbOps));
+    if (ops == nullptr) {
+        LERROR << "Error allocating memory for AvbOps";
+        return nullptr;
+    }
+
+    // We only need these operations since that's all what is being used
+    // by the avb_slot_verify(); Most of them are dummy operations because
+    // they're only required in bootloader but not required in user-space.
+    ops->read_from_partition = read_from_partition;
+    ops->read_rollback_index = dummy_read_rollback_index;
+    ops->validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
+    ops->read_is_device_unlocked = dummy_read_is_device_unlocked;
+    ops->get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
+
+    return ops;
+}
+
+void fs_mgr_dummy_avb_ops_free(AvbOps* ops) { free(ops); }
diff --git a/fs_mgr/fs_mgr_avb_ops.h b/fs_mgr/fs_mgr_avb_ops.h
new file mode 100644
index 0000000..bfdec9a
--- /dev/null
+++ b/fs_mgr/fs_mgr_avb_ops.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CORE_FS_MGR_AVB_OPS_H
+#define __CORE_FS_MGR_AVB_OPS_H
+
+#include <libavb/libavb.h>
+
+#include "fs_mgr.h"
+
+__BEGIN_DECLS
+
+/* Allocates a "dummy" AvbOps instance solely for use in user-space.
+ * Returns nullptr on OOM.
+ *
+ * It mainly provides read_from_partitions() for user-space to get
+ * AvbSlotVerifyData.vbmeta_images[] and the caller MUST check their
+ * integrity against the androidboot.vbmeta.{hash_alg, size, digest}
+ * values from /proc/cmdline, e.g. verify_vbmeta_images()
+ * in fs_mgr_avb.cpp.
+ *
+ * Other I/O operations are only required in boot loader so we set
+ * them as dummy operations here.
+ *  - Will allow any public key for signing.
+ *  - returns 0 for any rollback index location.
+ *  - returns device is unlocked regardless of the actual state.
+ *  - returns a dummy guid for any partition.
+ *
+ * Frees with fs_mgr_dummy_avb_ops_free().
+ */
+AvbOps* fs_mgr_dummy_avb_ops_new(struct fstab* fstab);
+
+/* Frees an AvbOps instance previously allocated with fs_mgr_avb_ops_new(). */
+void fs_mgr_dummy_avb_ops_free(AvbOps* ops);
+
+__END_DECLS
+
+#endif /* __CORE_FS_MGR_AVB_OPS_H */
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
new file mode 100644
index 0000000..5b2f218
--- /dev/null
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/properties.h>
+
+#include "fs_mgr_priv.h"
+
+// Tries to get the boot config value in properties, kernel cmdline and
+// device tree (in that order).  returns 'true' if successfully found, 'false'
+// otherwise
+bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
+    FS_MGR_CHECK(out_val != nullptr);
+
+    // first check if we have "ro.boot" property already
+    *out_val = android::base::GetProperty("ro.boot." + key, "");
+    if (!out_val->empty()) {
+        return true;
+    }
+
+    // fallback to kernel cmdline, properties may not be ready yet
+    std::string cmdline;
+    std::string cmdline_key("androidboot." + key);
+    if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
+        for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
+            std::vector<std::string> pieces = android::base::Split(entry, "=");
+            if (pieces.size() == 2) {
+                if (pieces[0] == cmdline_key) {
+                    *out_val = pieces[1];
+                    return true;
+                }
+            }
+        }
+    }
+
+    // lastly, check the device tree
+    if (is_dt_compatible()) {
+        std::string file_name = kAndroidDtDir + "/" + key;
+        // DT entries terminate with '\0' but so do the properties
+        if (android::base::ReadFileToString(file_name, out_val)) {
+            return true;
+        }
+
+        LERROR << "Error finding '" << key << "' in device tree";
+    }
+
+    return false;
+}
diff --git a/fs_mgr/fs_mgr_dm_ioctl.cpp b/fs_mgr/fs_mgr_dm_ioctl.cpp
new file mode 100644
index 0000000..4cbd5a8
--- /dev/null
+++ b/fs_mgr/fs_mgr_dm_ioctl.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <android-base/logging.h>
+#include <sys/ioctl.h>
+
+#include "fs_mgr_priv.h"
+#include "fs_mgr_priv_dm_ioctl.h"
+
+void fs_mgr_verity_ioctl_init(struct dm_ioctl* io, const std::string& name, unsigned flags) {
+    memset(io, 0, DM_BUF_SIZE);
+    io->data_size = DM_BUF_SIZE;
+    io->data_start = sizeof(struct dm_ioctl);
+    io->version[0] = 4;
+    io->version[1] = 0;
+    io->version[2] = 0;
+    io->flags = flags | DM_READONLY_FLAG;
+    if (!name.empty()) {
+        strlcpy(io->name, name.c_str(), sizeof(io->name));
+    }
+}
+
+bool fs_mgr_create_verity_device(struct dm_ioctl* io, const std::string& name, int fd) {
+    fs_mgr_verity_ioctl_init(io, name, 1);
+    if (ioctl(fd, DM_DEV_CREATE, io)) {
+        PERROR << "Error creating device mapping";
+        return false;
+    }
+    return true;
+}
+
+bool fs_mgr_destroy_verity_device(struct dm_ioctl* io, const std::string& name, int fd) {
+    fs_mgr_verity_ioctl_init(io, name, 0);
+    if (ioctl(fd, DM_DEV_REMOVE, io)) {
+        PERROR << "Error removing device mapping";
+        return false;
+    }
+    return true;
+}
+
+bool fs_mgr_get_verity_device_name(struct dm_ioctl* io, const std::string& name, int fd,
+                                   std::string* out_dev_name) {
+    FS_MGR_CHECK(out_dev_name != nullptr);
+
+    fs_mgr_verity_ioctl_init(io, name, 0);
+    if (ioctl(fd, DM_DEV_STATUS, io)) {
+        PERROR << "Error fetching verity device number";
+        return false;
+    }
+
+    int dev_num = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
+    *out_dev_name = "/dev/block/dm-" + std::to_string(dev_num);
+
+    return true;
+}
+
+bool fs_mgr_resume_verity_table(struct dm_ioctl* io, const std::string& name, int fd) {
+    fs_mgr_verity_ioctl_init(io, name, 0);
+    if (ioctl(fd, DM_DEV_SUSPEND, io)) {
+        PERROR << "Error activating verity device";
+        return false;
+    }
+    return true;
+}
diff --git a/fs_mgr/fs_mgr_format.c b/fs_mgr/fs_mgr_format.cpp
similarity index 83%
rename from fs_mgr/fs_mgr_format.c
rename to fs_mgr/fs_mgr_format.cpp
index 7c3b1ed..5705f93 100644
--- a/fs_mgr/fs_mgr_format.c
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -34,8 +34,10 @@
 #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)
 {
@@ -43,12 +45,12 @@
     int fd, rc = 0;
 
     if ((fd = open(fs_blkdev, O_WRONLY)) < 0) {
-        ERROR("Cannot open block device.  %s\n", strerror(errno));
+        PERROR << "Cannot open block device";
         return -1;
     }
 
     if ((ioctl(fd, BLKGETSIZE64, &dev_sz)) == -1) {
-        ERROR("Cannot get block device size.  %s\n", strerror(errno));
+        PERROR << "Cannot get block device size";
         close(fd);
         return -1;
     }
@@ -56,7 +58,7 @@
     struct selabel_handle *sehandle = selinux_android_file_context_handle();
     if (!sehandle) {
         /* libselinux logs specific error */
-        ERROR("Cannot initialize android file_contexts");
+        LERROR << "Cannot initialize android file_contexts";
         close(fd);
         return -1;
     }
@@ -71,7 +73,7 @@
     /* 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) {
-        ERROR("make_ext4fs returned %d.\n", rc);
+        LERROR << "make_ext4fs returned " << rc;
     }
     close(fd);
 
@@ -104,19 +106,19 @@
     for(;;) {
         pid_t p = waitpid(pid, &rc, 0);
         if (p != pid) {
-            ERROR("Error waiting for child process - %d\n", p);
+            LERROR << "Error waiting for child process - " << p;
             rc = -1;
             break;
         }
         if (WIFEXITED(rc)) {
             rc = WEXITSTATUS(rc);
-            INFO("%s done, status %d\n", args[0], rc);
+            LINFO << args[0] << " done, status " << rc;
             if (rc) {
                 rc = -1;
             }
             break;
         }
-        ERROR("Still waiting for %s...\n", args[0]);
+        LERROR << "Still waiting for " << args[0] << "...";
     }
 
     return rc;
@@ -126,14 +128,15 @@
 {
     int rc = -EINVAL;
 
-    ERROR("%s: Format %s as '%s'.\n", __func__, fstab->blk_device, fstab->fs_type);
+    LERROR << __FUNCTION__ << ": Format " << fstab->blk_device
+           << " as '" << fstab->fs_type << "'";
 
     if (!strncmp(fstab->fs_type, "f2fs", 4)) {
         rc = format_f2fs(fstab->blk_device);
     } else if (!strncmp(fstab->fs_type, "ext4", 4)) {
         rc = format_ext4(fstab->blk_device, fstab->mount_point, crypt_footer);
     } else {
-        ERROR("File system type '%s' is not supported\n", fstab->fs_type);
+        LERROR << "File system type '" << fstab->fs_type << "' is not supported";
     }
 
     return rc;
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
deleted file mode 100644
index f25d10c..0000000
--- a/fs_mgr/fs_mgr_fstab.c
+++ /dev/null
@@ -1,589 +0,0 @@
-/*
- * 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.
- */
-
-#include <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <unistd.h>
-
-#include "fs_mgr_priv.h"
-
-struct fs_mgr_flag_values {
-    char *key_loc;
-    char *verity_loc;
-    long long part_length;
-    char *label;
-    int partnum;
-    int swap_prio;
-    int max_comp_streams;
-    unsigned int zram_size;
-    uint64_t reserved_size;
-    unsigned int file_encryption_mode;
-};
-
-struct flag_list {
-    const char *name;
-    unsigned int flag;
-};
-
-static struct flag_list mount_flags[] = {
-    { "noatime",    MS_NOATIME },
-    { "noexec",     MS_NOEXEC },
-    { "nosuid",     MS_NOSUID },
-    { "nodev",      MS_NODEV },
-    { "nodiratime", MS_NODIRATIME },
-    { "ro",         MS_RDONLY },
-    { "rw",         0 },
-    { "remount",    MS_REMOUNT },
-    { "bind",       MS_BIND },
-    { "rec",        MS_REC },
-    { "unbindable", MS_UNBINDABLE },
-    { "private",    MS_PRIVATE },
-    { "slave",      MS_SLAVE },
-    { "shared",     MS_SHARED },
-    { "defaults",   0 },
-    { 0,            0 },
-};
-
-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 },
-    { "noemulatedsd", MF_NOEMULATEDSD },
-    { "notrim",       MF_NOTRIM },
-    { "formattable", MF_FORMATTABLE },
-    { "slotselect",  MF_SLOTSELECT },
-    { "nofail",      MF_NOFAIL },
-    { "latemount",   MF_LATEMOUNT },
-    { "reservedsize=", MF_RESERVEDSIZE },
-    { "defaults",    0 },
-    { 0,             0 },
-};
-
-#define EM_SOFTWARE 1
-#define EM_ICE      2
-
-static struct flag_list encryption_modes[] = {
-    {"software", EM_SOFTWARE},
-    {"ice", EM_ICE},
-    {0, 0}
-};
-
-static uint64_t calculate_zram_size(unsigned int percentage)
-{
-    uint64_t total;
-
-    total  = sysconf(_SC_PHYS_PAGES);
-    total *= percentage;
-    total /= 100;
-
-    total *= sysconf(_SC_PAGESIZE);
-
-    return total;
-}
-
-static uint64_t parse_size(const char *arg)
-{
-    char *endptr;
-    uint64_t size = strtoull(arg, &endptr, 10);
-    if (*endptr == 'k' || *endptr == 'K')
-        size *= 1024LL;
-    else if (*endptr == 'm' || *endptr == 'M')
-        size *= 1024LL * 1024LL;
-    else if (*endptr == 'g' || *endptr == 'G')
-        size *= 1024LL * 1024LL * 1024LL;
-
-    return size;
-}
-
-static int parse_flags(char *flags, struct flag_list *fl,
-                       struct fs_mgr_flag_values *flag_vals,
-                       char *fs_options, int fs_options_len)
-{
-    int f = 0;
-    int i;
-    char *p;
-    char *savep;
-
-    /* initialize flag values.  If we find a relevant flag, we'll
-     * update the value */
-    if (flag_vals) {
-        memset(flag_vals, 0, sizeof(*flag_vals));
-        flag_vals->partnum = -1;
-        flag_vals->swap_prio = -1; /* negative means it wasn't specified. */
-    }
-
-    /* initialize fs_options to the null string */
-    if (fs_options && (fs_options_len > 0)) {
-        fs_options[0] = '\0';
-    }
-
-    p = strtok_r(flags, ",", &savep);
-    while (p) {
-        /* Look for the flag "p" in the flag list "fl"
-         * If not found, the loop exits with fl[i].name being null.
-         */
-        for (i = 0; fl[i].name; i++) {
-            if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
-                f |= fl[i].flag;
-                if ((fl[i].flag == MF_CRYPT) && flag_vals) {
-                    /* The encryptable flag is followed by an = and the
-                     * location of the keys.  Get it and return it.
-                     */
-                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
-                } else if ((fl[i].flag == MF_VERIFY) && flag_vals) {
-                    /* If the verify flag is followed by an = and the
-                     * location for the verity state,  get it and return it.
-                     */
-                    char *start = strchr(p, '=');
-                    if (start) {
-                        flag_vals->verity_loc = strdup(start + 1);
-                    }
-                } else if ((fl[i].flag == MF_FORCECRYPT) && flag_vals) {
-                    /* The forceencrypt flag is followed by an = and the
-                     * location of the keys.  Get it and return it.
-                     */
-                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
-                } else if ((fl[i].flag == MF_FORCEFDEORFBE) && flag_vals) {
-                    /* The forcefdeorfbe flag is followed by an = and the
-                     * location of the keys.  Get it and return it.
-                     */
-                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
-                    flag_vals->file_encryption_mode = EM_SOFTWARE;
-                } else if ((fl[i].flag == MF_FILEENCRYPTION) && flag_vals) {
-                    /* The fileencryption flag is followed by an = and the
-                     * type of the encryption.  Get it and return it.
-                     */
-                    const struct flag_list *j;
-                    const char *mode = strchr(p, '=') + 1;
-                    for (j = encryption_modes; j->name; ++j) {
-                        if (!strcmp(mode, j->name)) {
-                            flag_vals->file_encryption_mode = j->flag;
-                        }
-                    }
-                    if (flag_vals->file_encryption_mode == 0) {
-                        ERROR("Unknown file encryption mode: %s\n", mode);
-                    }
-                } 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.
-                     */
-                    flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0);
-                } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) {
-                    /* The voldmanaged flag is followed by an = and the
-                     * label, a colon and the partition number or the
-                     * word "auto", e.g.
-                     *   voldmanaged=sdcard:3
-                     * Get and return them.
-                     */
-                    char *label_start;
-                    char *label_end;
-                    char *part_start;
-
-                    label_start = strchr(p, '=') + 1;
-                    label_end = strchr(p, ':');
-                    if (label_end) {
-                        flag_vals->label = strndup(label_start,
-                                                   (int) (label_end - label_start));
-                        part_start = strchr(p, ':') + 1;
-                        if (!strcmp(part_start, "auto")) {
-                            flag_vals->partnum = -1;
-                        } else {
-                            flag_vals->partnum = strtol(part_start, NULL, 0);
-                        }
-                    } else {
-                        ERROR("Warning: voldmanaged= flag malformed\n");
-                    }
-                } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
-                    flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
-                } else if ((fl[i].flag == MF_MAX_COMP_STREAMS) && flag_vals) {
-                    flag_vals->max_comp_streams = strtoll(strchr(p, '=') + 1, NULL, 0);
-                } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) {
-                    int is_percent = !!strrchr(p, '%');
-                    unsigned int val = strtoll(strchr(p, '=') + 1, NULL, 0);
-                    if (is_percent)
-                        flag_vals->zram_size = calculate_zram_size(val);
-                    else
-                        flag_vals->zram_size = val;
-                } else if ((fl[i].flag == MF_RESERVEDSIZE) && flag_vals) {
-                    /* The reserved flag is followed by an = and the
-                     * reserved size of the partition.  Get it and return it.
-                     */
-                    flag_vals->reserved_size = parse_size(strchr(p, '=') + 1);
-                }
-                break;
-            }
-        }
-
-        if (!fl[i].name) {
-            if (fs_options) {
-                /* It's not a known flag, so it must be a filesystem specific
-                 * option.  Add it to fs_options if it was passed in.
-                 */
-                strlcat(fs_options, p, fs_options_len);
-                strlcat(fs_options, ",", fs_options_len);
-            } else {
-                /* fs_options was not passed in, so if the flag is unknown
-                 * it's an error.
-                 */
-                ERROR("Warning: unknown flag %s\n", p);
-            }
-        }
-        p = strtok_r(NULL, ",", &savep);
-    }
-
-    if (fs_options && fs_options[0]) {
-        /* remove the last trailing comma from the list of options */
-        fs_options[strlen(fs_options) - 1] = '\0';
-    }
-
-    return f;
-}
-
-struct fstab *fs_mgr_read_fstab_file(FILE *fstab_file)
-{
-    int cnt, entries;
-    ssize_t len;
-    size_t alloc_len = 0;
-    char *line = NULL;
-    const char *delim = " \t";
-    char *save_ptr, *p;
-    struct fstab *fstab = NULL;
-    struct fs_mgr_flag_values flag_vals;
-#define FS_OPTIONS_LEN 1024
-    char tmp_fs_options[FS_OPTIONS_LEN];
-
-    entries = 0;
-    while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
-        /* if the last character is a newline, shorten the string by 1 byte */
-        if (line[len - 1] == '\n') {
-            line[len - 1] = '\0';
-        }
-        /* Skip any leading whitespace */
-        p = line;
-        while (isspace(*p)) {
-            p++;
-        }
-        /* ignore comments or empty lines */
-        if (*p == '#' || *p == '\0')
-            continue;
-        entries++;
-    }
-
-    if (!entries) {
-        ERROR("No entries found in fstab\n");
-        goto err;
-    }
-
-    /* Allocate and init the fstab structure */
-    fstab = calloc(1, sizeof(struct fstab));
-    fstab->num_entries = entries;
-    fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec));
-
-    fseek(fstab_file, 0, SEEK_SET);
-
-    cnt = 0;
-    while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
-        /* if the last character is a newline, shorten the string by 1 byte */
-        if (line[len - 1] == '\n') {
-            line[len - 1] = '\0';
-        }
-
-        /* Skip any leading whitespace */
-        p = line;
-        while (isspace(*p)) {
-            p++;
-        }
-        /* ignore comments or empty lines */
-        if (*p == '#' || *p == '\0')
-            continue;
-
-        /* If a non-comment entry is greater than the size we allocated, give an
-         * error and quit.  This can happen in the unlikely case the file changes
-         * between the two reads.
-         */
-        if (cnt >= entries) {
-            ERROR("Tried to process more entries than counted\n");
-            break;
-        }
-
-        if (!(p = strtok_r(line, delim, &save_ptr))) {
-            ERROR("Error parsing mount source\n");
-            goto err;
-        }
-        fstab->recs[cnt].blk_device = strdup(p);
-
-        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
-            ERROR("Error parsing mount_point\n");
-            goto err;
-        }
-        fstab->recs[cnt].mount_point = strdup(p);
-
-        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
-            ERROR("Error parsing fs_type\n");
-            goto err;
-        }
-        fstab->recs[cnt].fs_type = strdup(p);
-
-        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
-            ERROR("Error parsing mount_flags\n");
-            goto err;
-        }
-        tmp_fs_options[0] = '\0';
-        fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
-                                       tmp_fs_options, FS_OPTIONS_LEN);
-
-        /* fs_options are optional */
-        if (tmp_fs_options[0]) {
-            fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
-        } else {
-            fstab->recs[cnt].fs_options = NULL;
-        }
-
-        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
-            ERROR("Error parsing fs_mgr_options\n");
-            goto err;
-        }
-        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].verity_loc = flag_vals.verity_loc;
-        fstab->recs[cnt].length = flag_vals.part_length;
-        fstab->recs[cnt].label = flag_vals.label;
-        fstab->recs[cnt].partnum = flag_vals.partnum;
-        fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
-        fstab->recs[cnt].max_comp_streams = flag_vals.max_comp_streams;
-        fstab->recs[cnt].zram_size = flag_vals.zram_size;
-        fstab->recs[cnt].reserved_size = flag_vals.reserved_size;
-        fstab->recs[cnt].file_encryption_mode = flag_vals.file_encryption_mode;
-        cnt++;
-    }
-    /* If an A/B partition, modify block device to be the real block device */
-    if (fs_mgr_update_for_slotselect(fstab) != 0) {
-        ERROR("Error updating for slotselect\n");
-        goto err;
-    }
-    free(line);
-    return fstab;
-
-err:
-    free(line);
-    if (fstab)
-        fs_mgr_free_fstab(fstab);
-    return NULL;
-}
-
-struct fstab *fs_mgr_read_fstab(const char *fstab_path)
-{
-    FILE *fstab_file;
-    struct fstab *fstab;
-
-    fstab_file = fopen(fstab_path, "r");
-    if (!fstab_file) {
-        ERROR("Cannot open file %s\n", fstab_path);
-        return NULL;
-    }
-    fstab = fs_mgr_read_fstab_file(fstab_file);
-    if (fstab) {
-        fstab->fstab_filename = strdup(fstab_path);
-    }
-    fclose(fstab_file);
-    return fstab;
-}
-
-void fs_mgr_free_fstab(struct fstab *fstab)
-{
-    int i;
-
-    if (!fstab) {
-        return;
-    }
-
-    for (i = 0; i < fstab->num_entries; i++) {
-        /* Free the pointers return by strdup(3) */
-        free(fstab->recs[i].blk_device);
-        free(fstab->recs[i].mount_point);
-        free(fstab->recs[i].fs_type);
-        free(fstab->recs[i].fs_options);
-        free(fstab->recs[i].key_loc);
-        free(fstab->recs[i].label);
-    }
-
-    /* Free the fstab_recs array created by calloc(3) */
-    free(fstab->recs);
-
-    /* Free the fstab filename */
-    free(fstab->fstab_filename);
-
-    /* Free fstab */
-    free(fstab);
-}
-
-/* Add an entry to the fstab, and return 0 on success or -1 on error */
-int fs_mgr_add_entry(struct fstab *fstab,
-                     const char *mount_point, const char *fs_type,
-                     const char *blk_device)
-{
-    struct fstab_rec *new_fstab_recs;
-    int n = fstab->num_entries;
-
-    new_fstab_recs = (struct fstab_rec *)
-                     realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1));
-
-    if (!new_fstab_recs) {
-        return -1;
-    }
-
-    /* A new entry was added, so initialize it */
-     memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec));
-     new_fstab_recs[n].mount_point = strdup(mount_point);
-     new_fstab_recs[n].fs_type = strdup(fs_type);
-     new_fstab_recs[n].blk_device = strdup(blk_device);
-     new_fstab_recs[n].length = 0;
-
-     /* Update the fstab struct */
-     fstab->recs = new_fstab_recs;
-     fstab->num_entries++;
-
-     return 0;
-}
-
-/*
- * Returns the 1st matching fstab_rec that follows the start_rec.
- * start_rec is the result of a previous search or NULL.
- */
-struct fstab_rec *fs_mgr_get_entry_for_mount_point_after(struct fstab_rec *start_rec, struct fstab *fstab, const char *path)
-{
-    int i;
-    if (!fstab) {
-        return NULL;
-    }
-
-    if (start_rec) {
-        for (i = 0; i < fstab->num_entries; i++) {
-            if (&fstab->recs[i] == start_rec) {
-                i++;
-                break;
-            }
-        }
-    } else {
-        i = 0;
-    }
-    for (; i < fstab->num_entries; i++) {
-        int len = strlen(fstab->recs[i].mount_point);
-        if (strncmp(path, fstab->recs[i].mount_point, len) == 0 &&
-            (path[len] == '\0' || path[len] == '/')) {
-            return &fstab->recs[i];
-        }
-    }
-    return NULL;
-}
-
-/*
- * Returns the 1st matching mount point.
- * There might be more. To look for others, use fs_mgr_get_entry_for_mount_point_after()
- * and give the fstab_rec from the previous search.
- */
-struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
-{
-    return fs_mgr_get_entry_for_mount_point_after(NULL, fstab, path);
-}
-
-int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_VOLDMANAGED;
-}
-
-int fs_mgr_is_nonremovable(const struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_NONREMOVABLE;
-}
-
-int fs_mgr_is_verified(const struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_VERIFY;
-}
-
-int fs_mgr_is_encryptable(const struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE);
-}
-
-int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_FILEENCRYPTION;
-}
-
-const char* fs_mgr_get_file_encryption_mode(const struct fstab_rec *fstab)
-{
-    const struct flag_list *j;
-    for (j = encryption_modes; j->name; ++j) {
-        if (fstab->file_encryption_mode == j->flag) {
-            return j->name;
-        }
-    }
-    return NULL;
-}
-
-int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_FORCEFDEORFBE;
-}
-
-int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
-}
-
-int fs_mgr_is_notrim(struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_NOTRIM;
-}
-
-int fs_mgr_is_formattable(struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & (MF_FORMATTABLE);
-}
-
-int fs_mgr_is_slotselect(struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_SLOTSELECT;
-}
-
-int fs_mgr_is_nofail(struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_NOFAIL;
-}
-
-int fs_mgr_is_latemount(struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_LATEMOUNT;
-}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
new file mode 100644
index 0000000..de74685
--- /dev/null
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -0,0 +1,861 @@
+/*
+ * 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.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "fs_mgr_priv.h"
+
+struct fs_mgr_flag_values {
+    char *key_loc;
+    char *verity_loc;
+    long long part_length;
+    char *label;
+    int partnum;
+    int swap_prio;
+    int max_comp_streams;
+    unsigned int zram_size;
+    uint64_t reserved_size;
+    unsigned int file_contents_mode;
+    unsigned int file_names_mode;
+    unsigned int erase_blk_size;
+    unsigned int logical_blk_size;
+};
+
+struct flag_list {
+    const char *name;
+    unsigned int flag;
+};
+
+static struct flag_list mount_flags[] = {
+    { "noatime",    MS_NOATIME },
+    { "noexec",     MS_NOEXEC },
+    { "nosuid",     MS_NOSUID },
+    { "nodev",      MS_NODEV },
+    { "nodiratime", MS_NODIRATIME },
+    { "ro",         MS_RDONLY },
+    { "rw",         0 },
+    { "remount",    MS_REMOUNT },
+    { "bind",       MS_BIND },
+    { "rec",        MS_REC },
+    { "unbindable", MS_UNBINDABLE },
+    { "private",    MS_PRIVATE },
+    { "slave",      MS_SLAVE },
+    { "shared",     MS_SHARED },
+    { "defaults",   0 },
+    { 0,            0 },
+};
+
+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 },
+};
+
+#define EM_AES_256_XTS  1
+#define EM_ICE          2
+#define EM_AES_256_CTS  3
+#define EM_AES_256_HEH  4
+
+static const struct flag_list file_contents_encryption_modes[] = {
+    {"aes-256-xts", EM_AES_256_XTS},
+    {"software", EM_AES_256_XTS}, /* alias for backwards compatibility */
+    {"ice", EM_ICE}, /* hardware-specific inline cryptographic engine */
+    {0, 0},
+};
+
+static const struct flag_list file_names_encryption_modes[] = {
+    {"aes-256-cts", EM_AES_256_CTS},
+    {"aes-256-heh", EM_AES_256_HEH},
+    {0, 0},
+};
+
+static unsigned int encryption_mode_to_flag(const struct flag_list *list,
+                                            const char *mode, const char *type)
+{
+    const struct flag_list *j;
+
+    for (j = list; j->name; ++j) {
+        if (!strcmp(mode, j->name)) {
+            return j->flag;
+        }
+    }
+    LERROR << "Unknown " << type << " encryption mode: " << mode;
+    return 0;
+}
+
+static const char *flag_to_encryption_mode(const struct flag_list *list,
+                                           unsigned int flag)
+{
+    const struct flag_list *j;
+
+    for (j = list; j->name; ++j) {
+        if (flag == j->flag) {
+            return j->name;
+        }
+    }
+    return nullptr;
+}
+
+static uint64_t calculate_zram_size(unsigned int percentage)
+{
+    uint64_t total;
+
+    total  = sysconf(_SC_PHYS_PAGES);
+    total *= percentage;
+    total /= 100;
+
+    total *= sysconf(_SC_PAGESIZE);
+
+    return total;
+}
+
+static uint64_t parse_size(const char *arg)
+{
+    char *endptr;
+    uint64_t size = strtoull(arg, &endptr, 10);
+    if (*endptr == 'k' || *endptr == 'K')
+        size *= 1024LL;
+    else if (*endptr == 'm' || *endptr == 'M')
+        size *= 1024LL * 1024LL;
+    else if (*endptr == 'g' || *endptr == 'G')
+        size *= 1024LL * 1024LL * 1024LL;
+
+    return size;
+}
+
+/* fills 'dt_value' with the underlying device tree value string without
+ * the trailing '\0'. Returns true if 'dt_value' has a valid string, 'false'
+ * otherwise.
+ */
+static bool read_dt_file(const std::string& file_name, std::string* dt_value)
+{
+    if (android::base::ReadFileToString(file_name, dt_value)) {
+        if (!dt_value->empty()) {
+            // trim the trailing '\0' out, otherwise the comparison
+            // will produce false-negatives.
+            dt_value->resize(dt_value->size() - 1);
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static int parse_flags(char *flags, struct flag_list *fl,
+                       struct fs_mgr_flag_values *flag_vals,
+                       char *fs_options, int fs_options_len)
+{
+    int f = 0;
+    int i;
+    char *p;
+    char *savep;
+
+    /* initialize flag values.  If we find a relevant flag, we'll
+     * update the value */
+    if (flag_vals) {
+        memset(flag_vals, 0, sizeof(*flag_vals));
+        flag_vals->partnum = -1;
+        flag_vals->swap_prio = -1; /* negative means it wasn't specified. */
+    }
+
+    /* initialize fs_options to the null string */
+    if (fs_options && (fs_options_len > 0)) {
+        fs_options[0] = '\0';
+    }
+
+    p = strtok_r(flags, ",", &savep);
+    while (p) {
+        /* Look for the flag "p" in the flag list "fl"
+         * If not found, the loop exits with fl[i].name being null.
+         */
+        for (i = 0; fl[i].name; i++) {
+            if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
+                f |= fl[i].flag;
+                if ((fl[i].flag == MF_CRYPT) && flag_vals) {
+                    /* The encryptable flag is followed by an = and the
+                     * location of the keys.  Get it and return it.
+                     */
+                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
+                } else if ((fl[i].flag == MF_VERIFY) && flag_vals) {
+                    /* If the verify flag is followed by an = and the
+                     * location for the verity state,  get it and return it.
+                     */
+                    char *start = strchr(p, '=');
+                    if (start) {
+                        flag_vals->verity_loc = strdup(start + 1);
+                    }
+                } else if ((fl[i].flag == MF_FORCECRYPT) && flag_vals) {
+                    /* The forceencrypt flag is followed by an = and the
+                     * location of the keys.  Get it and return it.
+                     */
+                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
+                } else if ((fl[i].flag == MF_FORCEFDEORFBE) && flag_vals) {
+                    /* The forcefdeorfbe flag is followed by an = and the
+                     * location of the keys.  Get it and return it.
+                     */
+                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
+                    flag_vals->file_contents_mode = EM_AES_256_XTS;
+                    flag_vals->file_names_mode = EM_AES_256_CTS;
+                } else if ((fl[i].flag == MF_FILEENCRYPTION) && flag_vals) {
+                    /* The fileencryption flag is followed by an = and
+                     * the mode of contents encryption, then optionally a
+                     * : and the mode of filenames encryption (defaults
+                     * to aes-256-cts).  Get it and return it.
+                     */
+                    char *mode = strchr(p, '=') + 1;
+                    char *colon = strchr(mode, ':');
+                    if (colon) {
+                        *colon = '\0';
+                    }
+                    flag_vals->file_contents_mode =
+                        encryption_mode_to_flag(file_contents_encryption_modes,
+                                                mode, "file contents");
+                    if (colon) {
+                        flag_vals->file_names_mode =
+                            encryption_mode_to_flag(file_names_encryption_modes,
+                                                    colon + 1, "file names");
+                    } else {
+                        flag_vals->file_names_mode = EM_AES_256_CTS;
+                    }
+                } 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.
+                     */
+                    flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0);
+                } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) {
+                    /* The voldmanaged flag is followed by an = and the
+                     * label, a colon and the partition number or the
+                     * word "auto", e.g.
+                     *   voldmanaged=sdcard:3
+                     * Get and return them.
+                     */
+                    char *label_start;
+                    char *label_end;
+                    char *part_start;
+
+                    label_start = strchr(p, '=') + 1;
+                    label_end = strchr(p, ':');
+                    if (label_end) {
+                        flag_vals->label = strndup(label_start,
+                                                   (int) (label_end - label_start));
+                        part_start = strchr(p, ':') + 1;
+                        if (!strcmp(part_start, "auto")) {
+                            flag_vals->partnum = -1;
+                        } else {
+                            flag_vals->partnum = strtol(part_start, NULL, 0);
+                        }
+                    } else {
+                        LERROR << "Warning: voldmanaged= flag malformed";
+                    }
+                } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
+                    flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
+                } else if ((fl[i].flag == MF_MAX_COMP_STREAMS) && flag_vals) {
+                    flag_vals->max_comp_streams = strtoll(strchr(p, '=') + 1, NULL, 0);
+                } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) {
+                    int is_percent = !!strrchr(p, '%');
+                    unsigned int val = strtoll(strchr(p, '=') + 1, NULL, 0);
+                    if (is_percent)
+                        flag_vals->zram_size = calculate_zram_size(val);
+                    else
+                        flag_vals->zram_size = val;
+                } else if ((fl[i].flag == MF_RESERVEDSIZE) && flag_vals) {
+                    /* The reserved flag is followed by an = and the
+                     * reserved size of the partition.  Get it and return it.
+                     */
+                    flag_vals->reserved_size = parse_size(strchr(p, '=') + 1);
+                } else if ((fl[i].flag == MF_ERASEBLKSIZE) && flag_vals) {
+                    /* The erase block size flag is followed by an = and the flash
+                     * erase block size. Get it, check that it is a power of 2 and
+                     * at least 4096, and return it.
+                     */
+                    unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
+                    if (val >= 4096 && (val & (val - 1)) == 0)
+                        flag_vals->erase_blk_size = val;
+                } else if ((fl[i].flag == MF_LOGICALBLKSIZE) && flag_vals) {
+                    /* The logical block size flag is followed by an = and the flash
+                     * logical block size. Get it, check that it is a power of 2 and
+                     * at least 4096, and return it.
+                     */
+                    unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
+                    if (val >= 4096 && (val & (val - 1)) == 0)
+                        flag_vals->logical_blk_size = val;
+                }
+                break;
+            }
+        }
+
+        if (!fl[i].name) {
+            if (fs_options) {
+                /* It's not a known flag, so it must be a filesystem specific
+                 * option.  Add it to fs_options if it was passed in.
+                 */
+                strlcat(fs_options, p, fs_options_len);
+                strlcat(fs_options, ",", fs_options_len);
+            } else {
+                /* fs_options was not passed in, so if the flag is unknown
+                 * it's an error.
+                 */
+                LERROR << "Warning: unknown flag " << p;
+            }
+        }
+        p = strtok_r(NULL, ",", &savep);
+    }
+
+    if (fs_options && fs_options[0]) {
+        /* remove the last trailing comma from the list of options */
+        fs_options[strlen(fs_options) - 1] = '\0';
+    }
+
+    return f;
+}
+
+static bool is_dt_fstab_compatible() {
+    std::string dt_value;
+    std::string file_name = kAndroidDtDir + "/fstab/compatible";
+    if (read_dt_file(file_name, &dt_value)) {
+        if (dt_value == "android,fstab") {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static std::string read_fstab_from_dt() {
+    std::string fstab;
+    if (!is_dt_compatible() || !is_dt_fstab_compatible()) {
+        return fstab;
+    }
+
+    std::string fstabdir_name = kAndroidDtDir + "/fstab";
+    std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
+    if (!fstabdir) return fstab;
+
+    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;
+        }
+
+        // create <dev> <mnt_point>  <type>  <mnt_flags>  <fsmgr_flags>\n
+        std::vector<std::string> fstab_entry;
+        std::string file_name;
+        std::string value;
+        file_name = android::base::StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
+        if (!read_dt_file(file_name, &value)) {
+            LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
+            fstab.clear();
+            break;
+        }
+        fstab_entry.push_back(value);
+        fstab_entry.push_back(android::base::StringPrintf("/%s", dp->d_name));
+
+        file_name = android::base::StringPrintf("%s/%s/type", fstabdir_name.c_str(), dp->d_name);
+        if (!read_dt_file(file_name, &value)) {
+            LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
+            fstab.clear();
+            break;
+        }
+        fstab_entry.push_back(value);
+
+        file_name = android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
+        if (!read_dt_file(file_name, &value)) {
+            LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
+            fstab.clear();
+            break;
+        }
+        fstab_entry.push_back(value);
+
+        file_name = android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
+        if (!read_dt_file(file_name, &value)) {
+            LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
+            fstab.clear();
+            break;
+        }
+        fstab_entry.push_back(value);
+
+        fstab += android::base::Join(fstab_entry, " ");
+        fstab += '\n';
+    }
+
+    return fstab;
+}
+
+bool is_dt_compatible() {
+    std::string file_name = kAndroidDtDir + "/compatible";
+    std::string dt_value;
+    if (read_dt_file(file_name, &dt_value)) {
+        if (dt_value == "android,firmware") {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+struct fstab *fs_mgr_read_fstab_file(FILE *fstab_file)
+{
+    int cnt, entries;
+    ssize_t len;
+    size_t alloc_len = 0;
+    char *line = NULL;
+    const char *delim = " \t";
+    char *save_ptr, *p;
+    struct fstab *fstab = NULL;
+    struct fs_mgr_flag_values flag_vals;
+#define FS_OPTIONS_LEN 1024
+    char tmp_fs_options[FS_OPTIONS_LEN];
+
+    entries = 0;
+    while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
+        /* if the last character is a newline, shorten the string by 1 byte */
+        if (line[len - 1] == '\n') {
+            line[len - 1] = '\0';
+        }
+        /* Skip any leading whitespace */
+        p = line;
+        while (isspace(*p)) {
+            p++;
+        }
+        /* ignore comments or empty lines */
+        if (*p == '#' || *p == '\0')
+            continue;
+        entries++;
+    }
+
+    if (!entries) {
+        LERROR << "No entries found in fstab";
+        goto err;
+    }
+
+    /* Allocate and init the fstab structure */
+    fstab = static_cast<struct fstab *>(calloc(1, sizeof(struct fstab)));
+    fstab->num_entries = entries;
+    fstab->recs = static_cast<struct fstab_rec *>(
+        calloc(fstab->num_entries, sizeof(struct fstab_rec)));
+
+    fseek(fstab_file, 0, SEEK_SET);
+
+    cnt = 0;
+    while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
+        /* if the last character is a newline, shorten the string by 1 byte */
+        if (line[len - 1] == '\n') {
+            line[len - 1] = '\0';
+        }
+
+        /* Skip any leading whitespace */
+        p = line;
+        while (isspace(*p)) {
+            p++;
+        }
+        /* ignore comments or empty lines */
+        if (*p == '#' || *p == '\0')
+            continue;
+
+        /* If a non-comment entry is greater than the size we allocated, give an
+         * error and quit.  This can happen in the unlikely case the file changes
+         * between the two reads.
+         */
+        if (cnt >= entries) {
+            LERROR << "Tried to process more entries than counted";
+            break;
+        }
+
+        if (!(p = strtok_r(line, delim, &save_ptr))) {
+            LERROR << "Error parsing mount source";
+            goto err;
+        }
+        fstab->recs[cnt].blk_device = strdup(p);
+
+        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+            LERROR << "Error parsing mount_point";
+            goto err;
+        }
+        fstab->recs[cnt].mount_point = strdup(p);
+
+        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+            LERROR << "Error parsing fs_type";
+            goto err;
+        }
+        fstab->recs[cnt].fs_type = strdup(p);
+
+        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+            LERROR << "Error parsing mount_flags";
+            goto err;
+        }
+        tmp_fs_options[0] = '\0';
+        fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
+                                       tmp_fs_options, FS_OPTIONS_LEN);
+
+        /* fs_options are optional */
+        if (tmp_fs_options[0]) {
+            fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
+        } else {
+            fstab->recs[cnt].fs_options = NULL;
+        }
+
+        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+            LERROR << "Error parsing fs_mgr_options";
+            goto err;
+        }
+        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].verity_loc = flag_vals.verity_loc;
+        fstab->recs[cnt].length = flag_vals.part_length;
+        fstab->recs[cnt].label = flag_vals.label;
+        fstab->recs[cnt].partnum = flag_vals.partnum;
+        fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
+        fstab->recs[cnt].max_comp_streams = flag_vals.max_comp_streams;
+        fstab->recs[cnt].zram_size = flag_vals.zram_size;
+        fstab->recs[cnt].reserved_size = flag_vals.reserved_size;
+        fstab->recs[cnt].file_contents_mode = flag_vals.file_contents_mode;
+        fstab->recs[cnt].file_names_mode = flag_vals.file_names_mode;
+        fstab->recs[cnt].erase_blk_size = flag_vals.erase_blk_size;
+        fstab->recs[cnt].logical_blk_size = flag_vals.logical_blk_size;
+        cnt++;
+    }
+    /* If an A/B partition, modify block device to be the real block device */
+    if (fs_mgr_update_for_slotselect(fstab) != 0) {
+        LERROR << "Error updating for slotselect";
+        goto err;
+    }
+    free(line);
+    return fstab;
+
+err:
+    free(line);
+    if (fstab)
+        fs_mgr_free_fstab(fstab);
+    return NULL;
+}
+
+struct fstab *fs_mgr_read_fstab(const char *fstab_path)
+{
+    FILE *fstab_file;
+    struct fstab *fstab;
+
+    fstab_file = fopen(fstab_path, "r");
+    if (!fstab_file) {
+        LERROR << "Cannot open file " << fstab_path;
+        return NULL;
+    }
+    fstab = fs_mgr_read_fstab_file(fstab_file);
+    if (fstab) {
+        fstab->fstab_filename = strdup(fstab_path);
+    }
+    fclose(fstab_file);
+    return fstab;
+}
+
+/* Returns fstab entries parsed from the device tree if they
+ * exist
+ */
+struct fstab *fs_mgr_read_fstab_dt()
+{
+    std::string fstab_buf = read_fstab_from_dt();
+    if (fstab_buf.empty()) {
+        return NULL;
+    }
+
+    std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
+        fmemopen(static_cast<void*>(const_cast<char*>(fstab_buf.c_str())),
+                 fstab_buf.length(), "r"), fclose);
+    if (!fstab_file) {
+        return NULL;
+    }
+
+    struct fstab *fstab = fs_mgr_read_fstab_file(fstab_file.get());
+    if (!fstab) {
+        LERROR << "failed to load fstab from kernel:" << std::endl << fstab_buf;
+    }
+
+    return fstab;
+}
+
+/* combines fstab entries passed in from device tree with
+ * the ones found in /fstab.<hardware>
+ */
+struct fstab *fs_mgr_read_fstab_default()
+{
+    struct fstab *fstab = fs_mgr_read_fstab_dt();
+    std::string hw;
+    if (!fs_mgr_get_boot_config("hardware", &hw)) {
+        // if we fail to find this, return whatever was found in device tree
+        LWARNING << "failed to find device hardware name";
+        return fstab;
+    }
+
+    std::string default_fstab = FSTAB_PREFIX + hw;
+    struct fstab *f = fs_mgr_read_fstab(default_fstab.c_str());
+    if (!f) {
+        // return what we have
+        LWARNING << "failed to read fstab entries from '" << default_fstab << "'";
+        return fstab;
+    }
+
+    // return the fstab read from file if device tree doesn't
+    // have one, other wise merge the two
+    if (!fstab) {
+        fstab = f;
+    } else {
+        int total_entries = fstab->num_entries + f->num_entries;
+        fstab->recs = static_cast<struct fstab_rec *>(realloc(
+                        fstab->recs, total_entries * (sizeof(struct fstab_rec))));
+        if (!fstab->recs) {
+            LERROR << "failed to allocate fstab recs";
+            fstab->num_entries = 0;
+            fs_mgr_free_fstab(fstab);
+            return NULL;
+        }
+
+        for (int i = fstab->num_entries, j = 0; i < total_entries; i++, j++) {
+            // copy everything and *not* strdup
+            fstab->recs[i] = f->recs[j];
+        }
+
+        // free up fstab entries read from file, but don't cleanup
+        // the strings within f->recs[X] to make sure they are accessible
+        // through fstab->recs[X].
+        free(f->fstab_filename);
+        free(f);
+
+        fstab->num_entries = total_entries;
+    }
+
+    return fstab;
+}
+
+void fs_mgr_free_fstab(struct fstab *fstab)
+{
+    int i;
+
+    if (!fstab) {
+        return;
+    }
+
+    for (i = 0; i < fstab->num_entries; i++) {
+        /* Free the pointers return by strdup(3) */
+        free(fstab->recs[i].blk_device);
+        free(fstab->recs[i].mount_point);
+        free(fstab->recs[i].fs_type);
+        free(fstab->recs[i].fs_options);
+        free(fstab->recs[i].key_loc);
+        free(fstab->recs[i].label);
+    }
+
+    /* Free the fstab_recs array created by calloc(3) */
+    free(fstab->recs);
+
+    /* Free the fstab filename */
+    free(fstab->fstab_filename);
+
+    /* Free fstab */
+    free(fstab);
+}
+
+/* Add an entry to the fstab, and return 0 on success or -1 on error */
+int fs_mgr_add_entry(struct fstab *fstab,
+                     const char *mount_point, const char *fs_type,
+                     const char *blk_device)
+{
+    struct fstab_rec *new_fstab_recs;
+    int n = fstab->num_entries;
+
+    new_fstab_recs = (struct fstab_rec *)
+                     realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1));
+
+    if (!new_fstab_recs) {
+        return -1;
+    }
+
+    /* A new entry was added, so initialize it */
+     memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec));
+     new_fstab_recs[n].mount_point = strdup(mount_point);
+     new_fstab_recs[n].fs_type = strdup(fs_type);
+     new_fstab_recs[n].blk_device = strdup(blk_device);
+     new_fstab_recs[n].length = 0;
+
+     /* Update the fstab struct */
+     fstab->recs = new_fstab_recs;
+     fstab->num_entries++;
+
+     return 0;
+}
+
+/*
+ * Returns the 1st matching fstab_rec that follows the start_rec.
+ * start_rec is the result of a previous search or NULL.
+ */
+struct fstab_rec *fs_mgr_get_entry_for_mount_point_after(struct fstab_rec *start_rec, struct fstab *fstab, const char *path)
+{
+    int i;
+    if (!fstab) {
+        return NULL;
+    }
+
+    if (start_rec) {
+        for (i = 0; i < fstab->num_entries; i++) {
+            if (&fstab->recs[i] == start_rec) {
+                i++;
+                break;
+            }
+        }
+    } else {
+        i = 0;
+    }
+    for (; i < fstab->num_entries; i++) {
+        int len = strlen(fstab->recs[i].mount_point);
+        if (strncmp(path, fstab->recs[i].mount_point, len) == 0 &&
+            (path[len] == '\0' || path[len] == '/')) {
+            return &fstab->recs[i];
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Returns the 1st matching mount point.
+ * There might be more. To look for others, use fs_mgr_get_entry_for_mount_point_after()
+ * and give the fstab_rec from the previous search.
+ */
+struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
+{
+    return fs_mgr_get_entry_for_mount_point_after(NULL, fstab, path);
+}
+
+int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_VOLDMANAGED;
+}
+
+int fs_mgr_is_nonremovable(const struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_NONREMOVABLE;
+}
+
+int fs_mgr_is_verified(const struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_VERIFY;
+}
+
+int fs_mgr_is_verifyatboot(const struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_VERIFYATBOOT;
+}
+
+int fs_mgr_is_encryptable(const struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE);
+}
+
+int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_FILEENCRYPTION;
+}
+
+void fs_mgr_get_file_encryption_modes(const struct fstab_rec *fstab,
+                                      const char **contents_mode_ret,
+                                      const char **filenames_mode_ret)
+{
+    *contents_mode_ret = flag_to_encryption_mode(file_contents_encryption_modes,
+                                                 fstab->file_contents_mode);
+    *filenames_mode_ret = flag_to_encryption_mode(file_names_encryption_modes,
+                                                  fstab->file_names_mode);
+}
+
+int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_FORCEFDEORFBE;
+}
+
+int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
+}
+
+int fs_mgr_is_notrim(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_NOTRIM;
+}
+
+int fs_mgr_is_formattable(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & (MF_FORMATTABLE);
+}
+
+int fs_mgr_is_slotselect(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_SLOTSELECT;
+}
+
+int fs_mgr_is_nofail(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_NOFAIL;
+}
+
+int fs_mgr_is_latemount(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_LATEMOUNT;
+}
+
+int fs_mgr_is_quota(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_QUOTA;
+}
diff --git a/fs_mgr/fs_mgr_main.c b/fs_mgr/fs_mgr_main.cpp
similarity index 88%
rename from fs_mgr/fs_mgr_main.c
rename to fs_mgr/fs_mgr_main.cpp
index 4bfe202..f3919d9 100644
--- a/fs_mgr/fs_mgr_main.c
+++ b/fs_mgr/fs_mgr_main.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define _GNU_SOURCE
-
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -25,11 +23,12 @@
 #warning "libgen.h must not be included"
 #endif
 
-char *me = "";
+char *me = nullptr;
 
 static void usage(void)
 {
-    ERROR("%s: usage: %s <-a | -n mnt_point blk_dev | -u> <fstab_file>\n", me, me);
+    LERROR << me << ": usage: " << me
+           << " <-a | -n mnt_point blk_dev | -u> <fstab_file>";
     exit(1);
 }
 
@@ -90,7 +89,9 @@
     const char *fstab_file=NULL;
     struct fstab *fstab=NULL;
 
-    klog_set_level(6);
+    setenv("ANDROID_LOG_TAGS", "*:i", 1); // Set log level to INFO
+    android::base::InitLogging(
+        const_cast<char **>(argv), &android::base::KernelLogger);
 
     parse_options(argc, argv, &a_flag, &u_flag, &n_flag, &n_name, &n_blk_dev);
 
@@ -106,7 +107,7 @@
     } else if (u_flag) {
         return fs_mgr_unmount_all(fstab);
     } else {
-        ERROR("%s: Internal error, unknown option\n", me);
+        LERROR << me << ": Internal error, unknown option";
         exit(1);
     }
 
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 4632521..4e2ac8b 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -17,15 +17,34 @@
 #ifndef __CORE_FS_MGR_PRIV_H
 #define __CORE_FS_MGR_PRIV_H
 
-#include <cutils/klog.h>
+#include <android-base/logging.h>
 #include <fs_mgr.h>
+#include "fs_mgr_priv_boot_config.h"
+
+/* The CHECK() in logging.h will use program invocation name as the tag.
+ * Thus, the log will have prefix "init: " when libfs_mgr is statically
+ * linked in the init process. This might be opaque when debugging.
+ * Appends "in libfs_mgr" at the end of the abort message to explicitly
+ * indicate the check happens in fs_mgr.
+ */
+#define FS_MGR_CHECK(x) CHECK(x) << "in libfs_mgr "
+
+#define FS_MGR_TAG "[libfs_mgr]"
+
+// Logs a message to kernel
+#define LINFO    LOG(INFO) << FS_MGR_TAG
+#define LWARNING LOG(WARNING) << FS_MGR_TAG
+#define LERROR   LOG(ERROR) << FS_MGR_TAG
+
+// Logs a message with strerror(errno) at the end
+#define PINFO    PLOG(INFO) << FS_MGR_TAG
+#define PWARNING PLOG(WARNING) << FS_MGR_TAG
+#define PERROR   PLOG(ERROR) << FS_MGR_TAG
+
+const std::string FSTAB_PREFIX("/fstab.");
 
 __BEGIN_DECLS
 
-#define INFO(x...)    KLOG_INFO("fs_mgr", x)
-#define WARNING(x...) KLOG_WARNING("fs_mgr", x)
-#define ERROR(x...)   KLOG_ERROR("fs_mgr", x)
-
 #define CRYPTO_TMPFS_OPTIONS "size=256m,mode=0771,uid=1000,gid=1000"
 
 #define WAIT_TIMEOUT 20
@@ -65,34 +84,41 @@
  *
  */
 
-#define MF_WAIT         0x1
-#define MF_CHECK        0x2
-#define MF_CRYPT        0x4
-#define MF_NONREMOVABLE 0x8
-#define MF_VOLDMANAGED  0x10
-#define MF_LENGTH       0x20
-#define MF_RECOVERYONLY 0x40
-#define MF_SWAPPRIO     0x80
-#define MF_ZRAMSIZE     0x100
-#define MF_VERIFY       0x200
-#define MF_FORCECRYPT   0x400
-#define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only
-                                 external storage */
-#define MF_NOTRIM       0x1000
-#define MF_FILEENCRYPTION 0x2000
-#define MF_FORMATTABLE  0x4000
-#define MF_SLOTSELECT   0x8000
-#define MF_FORCEFDEORFBE 0x10000
-#define MF_LATEMOUNT    0x20000
-#define MF_NOFAIL       0x40000
-#define MF_VERIFYATBOOT 0x80000
+#define MF_WAIT                  0x1
+#define MF_CHECK                 0x2
+#define MF_CRYPT                 0x4
+#define MF_NONREMOVABLE          0x8
+#define MF_VOLDMANAGED          0x10
+#define MF_LENGTH               0x20
+#define MF_RECOVERYONLY         0x40
+#define MF_SWAPPRIO             0x80
+#define MF_ZRAMSIZE            0x100
+#define MF_VERIFY              0x200
+#define MF_FORCECRYPT          0x400
+#define MF_NOEMULATEDSD        0x800 /* no emulated sdcard daemon, sd card is the only
+                                        external storage */
+#define MF_NOTRIM             0x1000
+#define MF_FILEENCRYPTION     0x2000
+#define MF_FORMATTABLE        0x4000
+#define MF_SLOTSELECT         0x8000
+#define MF_FORCEFDEORFBE     0x10000
+#define MF_LATEMOUNT         0x20000
+#define MF_NOFAIL            0x40000
+#define MF_VERIFYATBOOT      0x80000
 #define MF_MAX_COMP_STREAMS 0x100000
-#define MF_RESERVEDSIZE 0x200000
+#define MF_RESERVEDSIZE     0x200000
+#define MF_QUOTA            0x400000
+#define MF_ERASEBLKSIZE     0x800000
+#define MF_LOGICALBLKSIZE  0X1000000
+#define MF_AVB             0X2000000
 
 #define DM_BUF_SIZE 4096
 
 int fs_mgr_set_blk_ro(const char *blockdev);
+int fs_mgr_test_access(const char *device);
 int fs_mgr_update_for_slotselect(struct fstab *fstab);
+bool is_dt_compatible();
+bool is_device_secure();
 
 __END_DECLS
 
diff --git a/fs_mgr/fs_mgr_priv_avb.h b/fs_mgr/fs_mgr_priv_avb.h
new file mode 100644
index 0000000..dce9f61
--- /dev/null
+++ b/fs_mgr/fs_mgr_priv_avb.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CORE_FS_MGR_PRIV_AVB_H
+#define __CORE_FS_MGR_PRIV_AVB_H
+
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+#include "fs_mgr.h"
+
+__BEGIN_DECLS
+
+#define FS_MGR_SETUP_AVB_HASHTREE_DISABLED (-2)
+#define FS_MGR_SETUP_AVB_FAIL (-1)
+#define FS_MGR_SETUP_AVB_SUCCESS 0
+
+bool fs_mgr_is_avb_used();
+
+/* Gets AVB metadata through external/avb/libavb for all partitions:
+ * AvbSlotVerifyData.vbmeta_images[] and checks their integrity
+ * against the androidboot.vbmeta.{hash_alg, size, digest} values
+ * from /proc/cmdline.
+ *
+ * Return values:
+ *   - FS_MGR_SETUP_AVB_SUCCESS: the metadata cab be trusted.
+ *   - FS_MGR_SETUP_AVB_FAIL: any error when reading and verifying the
+ *     metadata, e.g. I/O error, digest value mismatch, size mismatch.
+ *   - FS_MGR_SETUP_AVB_HASHTREE_DISABLED: 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.
+ */
+int fs_mgr_load_vbmeta_images(struct fstab* fstab);
+
+void fs_mgr_unload_vbmeta_images();
+
+int fs_mgr_setup_avb(struct fstab_rec* fstab_entry);
+
+__END_DECLS
+
+#endif /* __CORE_FS_MGR_PRIV_AVB_H */
diff --git a/fs_mgr/fs_mgr_priv_verity.h b/fs_mgr/fs_mgr_priv_boot_config.h
similarity index 63%
rename from fs_mgr/fs_mgr_priv_verity.h
rename to fs_mgr/fs_mgr_priv_boot_config.h
index d9e17bb..8773d33 100644
--- a/fs_mgr/fs_mgr_priv_verity.h
+++ b/fs_mgr/fs_mgr_priv_boot_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 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,14 +14,14 @@
  * limitations under the License.
  */
 
+#ifndef __CORE_FS_MGR_PRIV_BOOTCONFIG_H
+#define __CORE_FS_MGR_PRIV_BOOTCONFIG_H
+
 #include <sys/cdefs.h>
+#include <string>
 
-#define FS_MGR_SETUP_VERITY_DISABLED (-2)
-#define FS_MGR_SETUP_VERITY_FAIL (-1)
-#define FS_MGR_SETUP_VERITY_SUCCESS 0
+const std::string kAndroidDtDir("/proc/device-tree/firmware/android");
 
-__BEGIN_DECLS
+bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
 
-int fs_mgr_setup_verity(struct fstab_rec *fstab);
-
-__END_DECLS
+#endif /* __CORE_FS_MGR_PRIV_BOOTCONFIG_H */
diff --git a/fs_mgr/fs_mgr_priv_dm_ioctl.h b/fs_mgr/fs_mgr_priv_dm_ioctl.h
new file mode 100644
index 0000000..a00a9c1
--- /dev/null
+++ b/fs_mgr/fs_mgr_priv_dm_ioctl.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 __CORE_FS_MGR_PRIV_DM_IOCTL_H
+#define __CORE_FS_MGR_PRIV_DM_IOCTL_H
+
+#include <linux/dm-ioctl.h>
+#include <string>
+
+void fs_mgr_verity_ioctl_init(struct dm_ioctl* io, const std::string& name, unsigned flags);
+
+bool fs_mgr_create_verity_device(struct dm_ioctl* io, const std::string& name, int fd);
+
+bool fs_mgr_destroy_verity_device(struct dm_ioctl* io, const std::string& name, int fd);
+
+bool fs_mgr_get_verity_device_name(struct dm_ioctl* io, const std::string& name, int fd,
+                                   std::string* out_dev_name);
+
+bool fs_mgr_resume_verity_table(struct dm_ioctl* io, const std::string& name, int fd);
+
+#endif /* __CORE_FS_MGR_PRIV_DM_IOCTL_H */
diff --git a/fs_mgr/fs_mgr_priv_sha.h b/fs_mgr/fs_mgr_priv_sha.h
new file mode 100644
index 0000000..882411b
--- /dev/null
+++ b/fs_mgr/fs_mgr_priv_sha.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CORE_FS_MGR_PRIV_SHA_H
+#define __CORE_FS_MGR_PRIV_SHA_H
+
+#include <openssl/sha.h>
+
+class SHA256Hasher {
+   private:
+    SHA256_CTX sha256_ctx;
+    uint8_t hash[SHA256_DIGEST_LENGTH];
+
+   public:
+    enum { DIGEST_SIZE = SHA256_DIGEST_LENGTH };
+
+    SHA256Hasher() { SHA256_Init(&sha256_ctx); }
+
+    void update(const void* data, size_t data_size) { SHA256_Update(&sha256_ctx, data, data_size); }
+
+    const uint8_t* finalize() {
+        SHA256_Final(hash, &sha256_ctx);
+        return hash;
+    }
+};
+
+class SHA512Hasher {
+   private:
+    SHA512_CTX sha512_ctx;
+    uint8_t hash[SHA512_DIGEST_LENGTH];
+
+   public:
+    enum { DIGEST_SIZE = SHA512_DIGEST_LENGTH };
+
+    SHA512Hasher() { SHA512_Init(&sha512_ctx); }
+
+    void update(const uint8_t* data, size_t data_size) {
+        SHA512_Update(&sha512_ctx, data, data_size);
+    }
+
+    const uint8_t* finalize() {
+        SHA512_Final(hash, &sha512_ctx);
+        return hash;
+    }
+};
+
+#endif /* __CORE_FS_MGR_PRIV_SHA_H */
diff --git a/fs_mgr/fs_mgr_slotselect.c b/fs_mgr/fs_mgr_slotselect.c
deleted file mode 100644
index 0f59115..0000000
--- a/fs_mgr/fs_mgr_slotselect.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <cutils/properties.h>
-
-#include "fs_mgr.h"
-#include "fs_mgr_priv.h"
-
-#include "bootloader.h"
-
-// Copies slot_suffix from misc into |out_suffix|. Returns 0 on
-// success, -1 on error or if there is no non-empty slot_suffix.
-static int get_active_slot_suffix_from_misc(struct fstab *fstab,
-                                            char *out_suffix,
-                                            size_t suffix_len)
-{
-    int n;
-    int misc_fd;
-    ssize_t num_read;
-    struct bootloader_message_ab msg;
-
-    misc_fd = -1;
-    for (n = 0; n < fstab->num_entries; n++) {
-        if (strcmp(fstab->recs[n].mount_point, "/misc") == 0) {
-            misc_fd = open(fstab->recs[n].blk_device, O_RDONLY);
-            if (misc_fd == -1) {
-                ERROR("Error opening misc partition \"%s\" (%s)\n",
-                      fstab->recs[n].blk_device,
-                      strerror(errno));
-                return -1;
-            } else {
-                break;
-            }
-        }
-    }
-
-    if (misc_fd == -1) {
-        ERROR("Error finding misc partition\n");
-        return -1;
-    }
-
-    num_read = TEMP_FAILURE_RETRY(read(misc_fd, &msg, sizeof(msg)));
-    // Linux will never return partial reads when reading from block
-    // devices so no need to worry about them.
-    if (num_read != sizeof(msg)) {
-        ERROR("Error reading bootloader_message (%s)\n", strerror(errno));
-        close(misc_fd);
-        return -1;
-    }
-    close(misc_fd);
-    if (msg.slot_suffix[0] == '\0')
-        return -1;
-    strncpy(out_suffix, msg.slot_suffix, suffix_len);
-    return 0;
-}
-
-// Gets slot_suffix from either the kernel cmdline / firmware or the
-// misc partition. Sets |out_suffix| on success and returns 0. Returns
-// -1 if slot_suffix could not be determined.
-static int get_active_slot_suffix(struct fstab *fstab, char *out_suffix,
-                                  size_t suffix_len)
-{
-    char propbuf[PROPERTY_VALUE_MAX];
-
-    // Get the suffix from the kernel commandline (note that we don't
-    // allow the empty suffix). On bootloaders natively supporting A/B
-    // we'll hit this path every time so don't bother logging it.
-    property_get("ro.boot.slot_suffix", propbuf, "");
-    if (propbuf[0] != '\0') {
-        strncpy(out_suffix, propbuf, suffix_len);
-        return 0;
-    }
-
-    // If we couldn't get the suffix from the kernel cmdline, try the
-    // the misc partition.
-    if (get_active_slot_suffix_from_misc(fstab, out_suffix, suffix_len) == 0) {
-        INFO("Using slot suffix \"%s\" from misc\n", out_suffix);
-        return 0;
-    }
-
-    ERROR("Error determining slot_suffix\n");
-
-    return -1;
-}
-
-// Updates |fstab| for slot_suffix. Returns 0 on success, -1 on error.
-int fs_mgr_update_for_slotselect(struct fstab *fstab)
-{
-    int n;
-    char suffix[PROPERTY_VALUE_MAX];
-    int got_suffix = 0;
-
-    for (n = 0; n < fstab->num_entries; n++) {
-        if (fstab->recs[n].fs_mgr_flags & MF_SLOTSELECT) {
-            char *tmp;
-
-            if (!got_suffix) {
-                memset(suffix, '\0', sizeof(suffix));
-                if (get_active_slot_suffix(fstab, suffix,
-                                           sizeof(suffix) - 1) != 0) {
-                  return -1;
-                }
-                got_suffix = 1;
-            }
-
-            if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device,
-                         suffix) > 0) {
-                free(fstab->recs[n].blk_device);
-                fstab->recs[n].blk_device = tmp;
-            } else {
-                return -1;
-            }
-        }
-    }
-    return 0;
-}
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
new file mode 100644
index 0000000..f3bba7b
--- /dev/null
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#include "fs_mgr.h"
+#include "fs_mgr_priv.h"
+
+// Updates |fstab| for slot_suffix. Returns 0 on success, -1 on error.
+int fs_mgr_update_for_slotselect(struct fstab *fstab)
+{
+    int n;
+    int got_suffix = 0;
+    std::string suffix;
+
+    for (n = 0; n < fstab->num_entries; n++) {
+        if (fstab->recs[n].fs_mgr_flags & MF_SLOTSELECT) {
+            char *tmp;
+
+            if (!got_suffix) {
+                if (!fs_mgr_get_boot_config("slot_suffix", &suffix)) {
+                  return -1;
+                }
+                got_suffix = 1;
+            }
+
+            if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device,
+                         suffix.c_str()) > 0) {
+                free(fstab->recs[n].blk_device);
+                fstab->recs[n].blk_device = tmp;
+            } else {
+                return -1;
+            }
+        }
+    }
+    return 0;
+}
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 8c47b3c..54a6f71 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -30,6 +30,7 @@
 #include <unistd.h>
 
 #include <android-base/file.h>
+#include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <crypto_utils/android_pubkey.h>
@@ -44,9 +45,7 @@
 
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
-#include "fs_mgr_priv_verity.h"
-
-#define FSTAB_PREFIX "/fstab."
+#include "fs_mgr_priv_dm_ioctl.h"
 
 #define VERITY_TABLE_RSA_KEY "/verity_key"
 #define VERITY_TABLE_HASH_IDX 8
@@ -93,12 +92,12 @@
 
     FILE* f = fopen(path, "r");
     if (!f) {
-        ERROR("Can't open '%s'\n", path);
+        LERROR << "Can't open " << path;
         return NULL;
     }
 
     if (!fread(key_data, sizeof(key_data), 1, f)) {
-        ERROR("Could not read key!\n");
+        LERROR << "Could not read key!";
         fclose(f);
         return NULL;
     }
@@ -107,7 +106,7 @@
 
     RSA* key = NULL;
     if (!android_pubkey_decode(key_data, sizeof(key_data), &key)) {
-        ERROR("Could not parse key!\n");
+        LERROR << "Could not parse key!";
         return NULL;
     }
 
@@ -127,14 +126,14 @@
     // Now get the public key from the keyfile
     key = load_key(VERITY_TABLE_RSA_KEY);
     if (!key) {
-        ERROR("Couldn't load verity keys\n");
+        LERROR << "Couldn't load verity keys";
         goto out;
     }
 
     // verify the result
     if (!RSA_verify(NID_sha256, hash_buf, sizeof(hash_buf), signature,
                     signature_size, key)) {
-        ERROR("Couldn't verify table\n");
+        LERROR << "Couldn't verify table";
         goto out;
     }
 
@@ -184,55 +183,6 @@
     return -1;
 }
 
-static void verity_ioctl_init(struct dm_ioctl *io, const char *name, unsigned flags)
-{
-    memset(io, 0, DM_BUF_SIZE);
-    io->data_size = DM_BUF_SIZE;
-    io->data_start = sizeof(struct dm_ioctl);
-    io->version[0] = 4;
-    io->version[1] = 0;
-    io->version[2] = 0;
-    io->flags = flags | DM_READONLY_FLAG;
-    if (name) {
-        strlcpy(io->name, name, sizeof(io->name));
-    }
-}
-
-static int create_verity_device(struct dm_ioctl *io, char *name, int fd)
-{
-    verity_ioctl_init(io, name, 1);
-    if (ioctl(fd, DM_DEV_CREATE, io)) {
-        ERROR("Error creating device mapping (%s)", strerror(errno));
-        return -1;
-    }
-    return 0;
-}
-
-static int destroy_verity_device(struct dm_ioctl *io, char *name, int fd)
-{
-    verity_ioctl_init(io, name, 0);
-    if (ioctl(fd, DM_DEV_REMOVE, io)) {
-        ERROR("Error removing device mapping (%s)", strerror(errno));
-        return -1;
-    }
-    return 0;
-}
-
-static int get_verity_device_name(struct dm_ioctl *io, char *name, int fd, char **dev_name)
-{
-    verity_ioctl_init(io, name, 0);
-    if (ioctl(fd, DM_DEV_STATUS, io)) {
-        ERROR("Error fetching verity device number (%s)", strerror(errno));
-        return -1;
-    }
-    int dev_num = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
-    if (asprintf(dev_name, "/dev/block/dm-%u", dev_num) < 0) {
-        ERROR("Error getting verity block device name (%s)", strerror(errno));
-        return -1;
-    }
-    return 0;
-}
-
 struct verity_table_params {
     char *table;
     int mode;
@@ -275,7 +225,7 @@
     }
 
     if (res < 0 || (size_t)res >= bufsize) {
-        ERROR("Error building verity table; insufficient buffer size?\n");
+        LERROR << "Error building verity table; insufficient buffer size?";
         return false;
     }
 
@@ -294,21 +244,22 @@
     }
 
     if (res < 0 || (size_t)res >= bufsize) {
-        ERROR("Error building verity table; insufficient buffer size?\n");
+        LERROR << "Error building verity table; insufficient buffer size?";
         return false;
     }
 
     return true;
 }
 
-static int load_verity_table(struct dm_ioctl *io, char *name, uint64_t device_size, int fd,
+static int load_verity_table(struct dm_ioctl *io, const std::string &name,
+                             uint64_t device_size, int fd,
         const struct verity_table_params *params, format_verity_table_func format)
 {
     char *verity_params;
     char *buffer = (char*) io;
     size_t bufsize;
 
-    verity_ioctl_init(io, name, DM_STATUS_TABLE_FLAG);
+    fs_mgr_verity_ioctl_init(io, name, DM_STATUS_TABLE_FLAG);
 
     struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
 
@@ -324,47 +275,26 @@
     bufsize = DM_BUF_SIZE - (verity_params - buffer);
 
     if (!format(verity_params, bufsize, params)) {
-        ERROR("Failed to format verity parameters\n");
+        LERROR << "Failed to format verity parameters";
         return -1;
     }
 
-    INFO("loading verity table: '%s'", verity_params);
+    LINFO << "loading verity table: '" << verity_params << "'";
 
     // set next target boundary
     verity_params += strlen(verity_params) + 1;
-    verity_params = (char*)(((unsigned long)verity_params + 7) & ~8);
+    verity_params = (char*)(((uintptr_t)verity_params + 7) & ~7);
     tgt->next = verity_params - buffer;
 
     // send the ioctl to load the verity table
     if (ioctl(fd, DM_TABLE_LOAD, io)) {
-        ERROR("Error loading verity table (%s)\n", strerror(errno));
+        PERROR << "Error loading verity table";
         return -1;
     }
 
     return 0;
 }
 
-static int resume_verity_table(struct dm_ioctl *io, char *name, int fd)
-{
-    verity_ioctl_init(io, name, 0);
-    if (ioctl(fd, DM_DEV_SUSPEND, io)) {
-        ERROR("Error activating verity device (%s)", strerror(errno));
-        return -1;
-    }
-    return 0;
-}
-
-static int test_access(char *device) {
-    int tries = 25;
-    while (tries--) {
-        if (!access(device, F_OK) || errno != ENOENT) {
-            return 0;
-        }
-        usleep(40 * 1000);
-    }
-    return -1;
-}
-
 static int check_verity_restart(const char *fname)
 {
     char buffer[VERITY_KMSG_BUFSIZE + 1];
@@ -377,13 +307,13 @@
 
     if (fd == -1) {
         if (errno != ENOENT) {
-            ERROR("Failed to open %s (%s)\n", fname, strerror(errno));
+            PERROR << "Failed to open " << fname;
         }
         goto out;
     }
 
     if (fstat(fd, &s) == -1) {
-        ERROR("Failed to fstat %s (%s)\n", fname, strerror(errno));
+        PERROR << "Failed to fstat " << fname;
         goto out;
     }
 
@@ -394,14 +324,12 @@
     }
 
     if (lseek(fd, s.st_size - size, SEEK_SET) == -1) {
-        ERROR("Failed to lseek %jd %s (%s)\n", (intmax_t)(s.st_size - size), fname,
-            strerror(errno));
+        PERROR << "Failed to lseek " << (intmax_t)(s.st_size - size) << " " << fname;
         goto out;
     }
 
     if (!android::base::ReadFully(fd, buffer, size)) {
-        ERROR("Failed to read %zd bytes from %s (%s)\n", size, fname,
-            strerror(errno));
+        PERROR << "Failed to read " << size << " bytes from " << fname;
         goto out;
     }
 
@@ -473,14 +401,14 @@
     fp = fopen(fname, "r+");
 
     if (!fp) {
-        ERROR("Failed to open %s (%s)\n", fname, strerror(errno));
+        PERROR << "Failed to open " << fname;
         goto out;
     }
 
     /* check magic */
     if (fseek(fp, start, SEEK_SET) < 0 ||
         fread(&magic, sizeof(magic), 1, fp) != 1) {
-        ERROR("Failed to read magic from %s (%s)\n", fname, strerror(errno));
+        PERROR << "Failed to read magic from " << fname;
         goto out;
     }
 
@@ -489,13 +417,13 @@
 
         if (fseek(fp, start, SEEK_SET) < 0 ||
             fwrite(&magic, sizeof(magic), 1, fp) != 1) {
-            ERROR("Failed to write magic to %s (%s)\n", fname, strerror(errno));
+            PERROR << "Failed to write magic to " << fname;
             goto out;
         }
 
         rc = metadata_add(fp, start + sizeof(magic), stag, slength, offset);
         if (rc < 0) {
-            ERROR("Failed to add metadata to %s: %s\n", fname, strerror(errno));
+            PERROR << "Failed to add metadata to " << fname;
         }
 
         goto out;
@@ -520,14 +448,13 @@
             start += length;
 
             if (fseek(fp, length, SEEK_CUR) < 0) {
-                ERROR("Failed to seek %s (%s)\n", fname, strerror(errno));
+                PERROR << "Failed to seek " << fname;
                 goto out;
             }
         } else {
             rc = metadata_add(fp, start, stag, slength, offset);
             if (rc < 0) {
-                ERROR("Failed to write metadata to %s: %s\n", fname,
-                    strerror(errno));
+                PERROR << "Failed to write metadata to " << fname;
             }
             goto out;
         }
@@ -551,13 +478,13 @@
     fd = TEMP_FAILURE_RETRY(open(fname, O_WRONLY | O_SYNC | O_CLOEXEC));
 
     if (fd == -1) {
-        ERROR("Failed to open %s (%s)\n", fname, strerror(errno));
+        PERROR << "Failed to open " << fname;
         goto out;
     }
 
     if (TEMP_FAILURE_RETRY(pwrite64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
-        ERROR("Failed to write %zu bytes to %s to offset %" PRIu64 " (%s)\n",
-            sizeof(s), fname, offset, strerror(errno));
+        PERROR << "Failed to write " << sizeof(s) << " bytes to " << fname
+               << " to offset " << offset;
         goto out;
     }
 
@@ -580,13 +507,13 @@
     fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
 
     if (fd == -1) {
-        ERROR("Failed to open %s (%s)\n", fname, strerror(errno));
+        PERROR << "Failed to open " << fname;
         goto out;
     }
 
     if (TEMP_FAILURE_RETRY(pread64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
-        ERROR("Failed to read %zu bytes from %s offset %" PRIu64 " (%s)\n",
-            sizeof(s), fname, offset, strerror(errno));
+        PERROR << "Failed to read " <<  sizeof(s) << " bytes from " << fname
+               << " offset " << offset;
         goto out;
     }
 
@@ -598,13 +525,13 @@
     }
 
     if (s.version != VERITY_STATE_VERSION) {
-        ERROR("Unsupported verity state version (%u)\n", s.version);
+        LERROR << "Unsupported verity state version (" << s.version << ")";
         goto out;
     }
 
     if (s.mode < VERITY_MODE_EIO ||
         s.mode > VERITY_MODE_LAST) {
-        ERROR("Unsupported verity mode (%u)\n", s.mode);
+        LERROR << "Unsupported verity mode (" << s.mode << ")";
         goto out;
     }
 
@@ -626,15 +553,14 @@
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC)));
 
     if (fd == -1) {
-        ERROR("Failed to open %s: %s\n", path, strerror(errno));
+        PERROR << "Failed to open " << path;
         return -errno;
     }
 
     while (size) {
         size_read = TEMP_FAILURE_RETRY(read(fd, buf, READ_BUF_SIZE));
         if (size_read == -1) {
-            ERROR("Error in reading partition %s: %s\n", path,
-                  strerror(errno));
+            PERROR << "Error in reading partition " << path;
             return -errno;
         }
         size -= size_read;
@@ -658,15 +584,13 @@
 
     if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
             FEC_DEFAULT_ROOTS) == -1) {
-        ERROR("Failed to open '%s' (%s)\n", fstab->blk_device,
-            strerror(errno));
+        PERROR << "Failed to open '" << fstab->blk_device << "'";
         return rc;
     }
 
     // read verity metadata
     if (fec_verity_get_metadata(f, &verity) == -1) {
-        ERROR("Failed to get verity metadata '%s' (%s)\n", fstab->blk_device,
-            strerror(errno));
+        PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
         goto out;
     }
 
@@ -674,7 +598,7 @@
 
     if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s",
             basename(fstab->mount_point)) >= (int)sizeof(tag)) {
-        ERROR("Metadata tag name too long for %s\n", fstab->mount_point);
+        LERROR << "Metadata tag name too long for " << fstab->mount_point;
         goto out;
     }
 
@@ -686,14 +610,14 @@
     fd = TEMP_FAILURE_RETRY(open(fstab->verity_loc, O_RDWR | O_SYNC | O_CLOEXEC));
 
     if (fd == -1) {
-        ERROR("Failed to open %s: %s\n", fstab->verity_loc, strerror(errno));
+        PERROR << "Failed to open " << fstab->verity_loc;
         goto out;
     }
 
     if (TEMP_FAILURE_RETRY(pread64(fd, prev, sizeof(prev),
             offset)) != sizeof(prev)) {
-        ERROR("Failed to read %zu bytes from %s offset %" PRIu64 " (%s)\n",
-            sizeof(prev), fstab->verity_loc, offset, strerror(errno));
+        PERROR << "Failed to read " << sizeof(prev) << " bytes from "
+               << fstab->verity_loc << " offset " << offset;
         goto out;
     }
 
@@ -703,8 +627,8 @@
         /* update current signature hash */
         if (TEMP_FAILURE_RETRY(pwrite64(fd, curr, sizeof(curr),
                 offset)) != sizeof(curr)) {
-            ERROR("Failed to write %zu bytes to %s offset %" PRIu64 " (%s)\n",
-                sizeof(curr), fstab->verity_loc, offset, strerror(errno));
+            PERROR << "Failed to write " << sizeof(curr) << " bytes to "
+                   << fstab->verity_loc << " offset " << offset;
             goto out;
         }
     }
@@ -722,7 +646,7 @@
 
     if (snprintf(tag, sizeof(tag), VERITY_STATE_TAG "_%s",
             basename(fstab->mount_point)) >= (int)sizeof(tag)) {
-        ERROR("Metadata tag name too long for %s\n", fstab->mount_point);
+        LERROR << "Metadata tag name too long for " << fstab->mount_point;
         return -1;
     }
 
@@ -732,7 +656,6 @@
 
 static int load_verity_state(struct fstab_rec *fstab, int *mode)
 {
-    char propbuf[PROPERTY_VALUE_MAX];
     int match = 0;
     off64_t offset = 0;
 
@@ -740,10 +663,9 @@
     *mode = VERITY_MODE_EIO;
 
     /* use the kernel parameter if set */
-    property_get("ro.boot.veritymode", propbuf, "");
-
-    if (*propbuf != '\0') {
-        if (!strcmp(propbuf, "enforcing")) {
+    std::string veritymode;
+    if (fs_mgr_get_boot_config("veritymode", &veritymode)) {
+        if (veritymode.compare("enforcing")) {
             *mode = VERITY_MODE_DEFAULT;
         }
         return 0;
@@ -771,8 +693,6 @@
 
 int fs_mgr_load_verity_state(int *mode)
 {
-    char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
-    char propbuf[PROPERTY_VALUE_MAX];
     int rc = -1;
     int i;
     int current;
@@ -782,13 +702,9 @@
      * logging mode, in which case return that */
     *mode = VERITY_MODE_DEFAULT;
 
-    property_get("ro.hardware", propbuf, "");
-    snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
-
-    fstab = fs_mgr_read_fstab(fstab_filename);
-
+    fstab = fs_mgr_read_fstab_default();
     if (!fstab) {
-        ERROR("Failed to read %s\n", fstab_filename);
+        LERROR << "Failed to read default fstab";
         goto out;
     }
 
@@ -822,8 +738,7 @@
 {
     alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
     bool system_root = false;
-    char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
-    const char *mount_point;
+    std::string mount_point;
     char propbuf[PROPERTY_VALUE_MAX];
     const char *status;
     int fd = -1;
@@ -842,22 +757,16 @@
     }
 
     fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
-
     if (fd == -1) {
-        ERROR("Error opening device mapper (%s)\n", strerror(errno));
+        PERROR << "Error opening device mapper";
         goto out;
     }
 
-    property_get("ro.hardware", propbuf, "");
-    snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
-
     property_get("ro.build.system_root_image", propbuf, "");
     system_root = !strcmp(propbuf, "true");
-
-    fstab = fs_mgr_read_fstab(fstab_filename);
-
+    fstab = fs_mgr_read_fstab_default();
     if (!fstab) {
-        ERROR("Failed to read %s\n", fstab_filename);
+        LERROR << "Failed to read default fstab";
         goto out;
     }
 
@@ -872,14 +781,14 @@
             mount_point = basename(fstab->recs[i].mount_point);
         }
 
-        verity_ioctl_init(io, mount_point, 0);
+        fs_mgr_verity_ioctl_init(io, mount_point, 0);
 
         if (ioctl(fd, DM_TABLE_STATUS, io)) {
             if (fstab->recs[i].fs_mgr_flags & MF_VERIFYATBOOT) {
                 status = "V";
             } else {
-                ERROR("Failed to query DM_TABLE_STATUS for %s (%s)\n", mount_point,
-                      strerror(errno));
+                PERROR << "Failed to query DM_TABLE_STATUS for "
+                       << mount_point.c_str();
                 continue;
             }
         }
@@ -887,7 +796,7 @@
         status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
 
         if (*status == 'C' || *status == 'V') {
-            callback(&fstab->recs[i], mount_point, mode, *status);
+            callback(&fstab->recs[i], mount_point.c_str(), mode, *status);
         }
     }
 
@@ -933,38 +842,51 @@
     *table = strdup(result.c_str());
 }
 
-int fs_mgr_setup_verity(struct fstab_rec *fstab)
+// prepares the verity enabled (MF_VERIFY / MF_VERIFYATBOOT) fstab record for
+// mount. The 'wait_for_verity_dev' parameter makes this function wait for the
+// verity device to get created before return
+int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev)
 {
     int retval = FS_MGR_SETUP_VERITY_FAIL;
     int fd = -1;
-    char *verity_blk_name = NULL;
+    std::string verity_blk_name;
     struct fec_handle *f = NULL;
     struct fec_verity_metadata verity;
     struct verity_table_params params = { .table = NULL };
 
     alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
     struct dm_ioctl *io = (struct dm_ioctl *) buffer;
-    char *mount_point = basename(fstab->mount_point);
+    const std::string mount_point(basename(fstab->mount_point));
     bool verified_at_boot = false;
 
+    // This is a public API and so deserves its own check to see if verity
+    // setup is needed at all.
+    if (!is_device_secure()) {
+        LINFO << "Verity setup skipped for " << mount_point;
+        return FS_MGR_SETUP_VERITY_SUCCESS;
+    }
+
     if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
             FEC_DEFAULT_ROOTS) < 0) {
-        ERROR("Failed to open '%s' (%s)\n", fstab->blk_device,
-            strerror(errno));
+        PERROR << "Failed to open '" << fstab->blk_device << "'";
         return retval;
     }
 
     // read verity metadata
     if (fec_verity_get_metadata(f, &verity) < 0) {
-        ERROR("Failed to get verity metadata '%s' (%s)\n", fstab->blk_device,
-            strerror(errno));
+        PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
+        // Allow verity disabled when the device is unlocked without metadata
+        if ("0" == android::base::GetProperty("ro.boot.flash.locked", "")) {
+            retval = FS_MGR_SETUP_VERITY_DISABLED;
+            LWARNING << "Allow invalid metadata when the device is unlocked";
+        }
         goto out;
     }
 
 #ifdef ALLOW_ADBD_DISABLE_VERITY
     if (verity.disabled) {
         retval = FS_MGR_SETUP_VERITY_DISABLED;
-        INFO("Attempt to cleanly disable verity - only works in USERDEBUG\n");
+        LINFO << "Attempt to cleanly disable verity - only works in USERDEBUG";
         goto out;
     }
 #endif
@@ -978,19 +900,19 @@
 
     // get the device mapper fd
     if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
-        ERROR("Error opening device mapper (%s)\n", strerror(errno));
+        PERROR << "Error opening device mapper";
         goto out;
     }
 
     // create the device
-    if (create_verity_device(io, mount_point, fd) < 0) {
-        ERROR("Couldn't create verity device!\n");
+    if (!fs_mgr_create_verity_device(io, mount_point, fd)) {
+        LERROR << "Couldn't create verity device!";
         goto out;
     }
 
     // get the name of the device file
-    if (get_verity_device_name(io, mount_point, fd, &verity_blk_name) < 0) {
-        ERROR("Couldn't get verity device number!\n");
+    if (!fs_mgr_get_verity_device_name(io, mount_point, fd, &verity_blk_name)) {
+        LERROR << "Couldn't get verity device number!";
         goto out;
     }
 
@@ -1025,7 +947,8 @@
         }
     }
 
-    INFO("Enabling dm-verity for %s (mode %d)\n", mount_point, params.mode);
+    LINFO << "Enabling dm-verity for " << mount_point.c_str()
+          << " (mode " << params.mode << ")";
 
     if (fstab->fs_mgr_flags & MF_SLOTSELECT) {
         // Update the verity params using the actual block device path
@@ -1040,7 +963,7 @@
 
     if (params.ecc.valid) {
         // kernel may not support error correction, try without
-        INFO("Disabling error correction for %s\n", mount_point);
+        LINFO << "Disabling error correction for " << mount_point.c_str();
         params.ecc.valid = false;
 
         if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
@@ -1057,7 +980,7 @@
 
     if (params.mode != VERITY_MODE_EIO) {
         // as a last resort, EIO mode should always be supported
-        INFO("Falling back to EIO mode for %s\n", mount_point);
+        LINFO << "Falling back to EIO mode for " << mount_point.c_str();
         params.mode = VERITY_MODE_EIO;
 
         if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
@@ -1066,13 +989,13 @@
         }
     }
 
-    ERROR("Failed to load verity table for %s\n", mount_point);
+    LERROR << "Failed to load verity table for " << mount_point.c_str();
     goto out;
 
 loaded:
 
     // activate the device
-    if (resume_verity_table(io, mount_point, fd) < 0) {
+    if (!fs_mgr_resume_verity_table(io, mount_point, fd)) {
         goto out;
     }
 
@@ -1082,10 +1005,11 @@
     // Verify the entire partition in one go
     // If there is an error, allow it to mount as a normal verity partition.
     if (fstab->fs_mgr_flags & MF_VERIFYATBOOT) {
-        INFO("Verifying partition %s at boot\n", fstab->blk_device);
-        int err = read_partition(verity_blk_name, verity.data_size);
+        LINFO << "Verifying partition " << fstab->blk_device << " at boot";
+        int err = read_partition(verity_blk_name.c_str(), verity.data_size);
         if (!err) {
-            INFO("Verified verity partition %s at boot\n", fstab->blk_device);
+            LINFO << "Verified verity partition "
+                  << fstab->blk_device << " at boot";
             verified_at_boot = true;
         }
     }
@@ -1093,15 +1017,14 @@
     // assign the new verity block device as the block device
     if (!verified_at_boot) {
         free(fstab->blk_device);
-        fstab->blk_device = verity_blk_name;
-        verity_blk_name = 0;
-    } else if (destroy_verity_device(io, mount_point, fd) < 0) {
-        ERROR("Failed to remove verity device %s\n", mount_point);
+        fstab->blk_device = strdup(verity_blk_name.c_str());
+    } else if (!fs_mgr_destroy_verity_device(io, mount_point, fd)) {
+        LERROR << "Failed to remove verity device " << mount_point.c_str();
         goto out;
     }
 
     // make sure we've set everything up properly
-    if (test_access(fstab->blk_device) < 0) {
+    if (wait_for_verity_dev && fs_mgr_test_access(fstab->blk_device) < 0) {
         goto out;
     }
 
@@ -1114,7 +1037,6 @@
 
     fec_close(f);
     free(params.table);
-    free(verity_blk_name);
 
     return retval;
 }
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 8ecc93c..a014db5 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -76,13 +76,18 @@
     int max_comp_streams;
     unsigned int zram_size;
     uint64_t reserved_size;
-    unsigned int file_encryption_mode;
+    unsigned int file_contents_mode;
+    unsigned int file_names_mode;
+    unsigned int erase_blk_size;
+    unsigned int logical_blk_size;
 };
 
 // Callback function for verity status
 typedef void (*fs_mgr_verity_state_callback)(struct fstab_rec *fstab,
         const char *mount_point, int mode, int status);
 
+struct fstab *fs_mgr_read_fstab_default();
+struct fstab *fs_mgr_read_fstab_dt();
 struct fstab *fs_mgr_read_fstab_file(FILE *fstab_file);
 struct fstab *fs_mgr_read_fstab(const char *fstab_path);
 void fs_mgr_free_fstab(struct fstab *fstab);
@@ -101,7 +106,8 @@
 
 int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
                     char *tmp_mount_point);
-int fs_mgr_do_tmpfs_mount(char *n_name);
+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);
@@ -114,23 +120,28 @@
 int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab);
 int fs_mgr_is_nonremovable(const struct fstab_rec *fstab);
 int fs_mgr_is_verified(const struct fstab_rec *fstab);
+int fs_mgr_is_verifyatboot(const struct fstab_rec *fstab);
 int fs_mgr_is_encryptable(const struct fstab_rec *fstab);
 int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab);
-const char* fs_mgr_get_file_encryption_mode(const struct fstab_rec *fstab);
+void fs_mgr_get_file_encryption_modes(const struct fstab_rec *fstab,
+                                      const char **contents_mode_ret,
+                                      const char **filenames_mode_ret);
 int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab);
 int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab);
 int fs_mgr_is_notrim(struct fstab_rec *fstab);
 int fs_mgr_is_formattable(struct fstab_rec *fstab);
+int fs_mgr_is_slotselect(struct fstab_rec *fstab);
 int fs_mgr_is_nofail(struct fstab_rec *fstab);
 int fs_mgr_is_latemount(struct fstab_rec *fstab);
+int fs_mgr_is_quota(struct fstab_rec *fstab);
 int fs_mgr_swapon_all(struct fstab *fstab);
 
 int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
 
-#define FS_MGR_EARLY_SETUP_VERITY_NO_VERITY -2
-#define FS_MGR_EARLY_SETUP_VERITY_FAIL -1
-#define FS_MGR_EARLY_SETUP_VERITY_SUCCESS 0
-int fs_mgr_early_setup_verity(struct fstab_rec *fstab);
+#define FS_MGR_SETUP_VERITY_DISABLED (-2)
+#define FS_MGR_SETUP_VERITY_FAIL (-1)
+#define FS_MGR_SETUP_VERITY_SUCCESS 0
+int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev);
 
 #ifdef __cplusplus
 }
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h
index 8b15d72..cb02a6f 100644
--- a/gatekeeperd/SoftGateKeeper.h
+++ b/gatekeeperd/SoftGateKeeper.h
@@ -152,7 +152,7 @@
     }
 
     bool DoVerify(const password_handle_t *expected_handle, const SizedBuffer &password) {
-        uint64_t user_id = android::base::get_unaligned(&expected_handle->user_id);
+        uint64_t user_id = android::base::get_unaligned<secure_id_t>(&expected_handle->user_id);
         FastHashMap::const_iterator it = fast_hash_map_.find(user_id);
         if (it != fast_hash_map_.end() && VerifyFast(it->second, password)) {
             return true;
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index d2c119d..e6eb3bc 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -24,7 +24,6 @@
 #include <stdint.h>
 #include <unistd.h>
 
-#include <android/log.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
@@ -33,6 +32,7 @@
 #include <hardware/hw_auth_token.h>
 #include <keystore/IKeystoreService.h>
 #include <keystore/keystore.h> // For error code
+#include <log/log.h>
 #include <utils/Log.h>
 #include <utils/String16.h>
 
@@ -56,7 +56,7 @@
 class GateKeeperProxy : public BnGateKeeperService {
 public:
     GateKeeperProxy() {
-        hw_device = IGatekeeper::getService("gatekeeper");
+        hw_device = IGatekeeper::getService();
 
         if (hw_device == nullptr) {
             ALOGW("falling back to software GateKeeper");
@@ -184,7 +184,7 @@
                     ret = rsp.timeout;
                 }
             });
-            if (!hwRes.getStatus().isOk()) {
+            if (!hwRes.isOk()) {
                 ALOGE("enroll transaction failed\n");
                 ret = -1;
             }
@@ -196,7 +196,14 @@
                     enrolled_password_handle, enrolled_password_handle_length);
         }
 
-        if (ret == 0) {
+        if (ret == GATEKEEPER_RESPONSE_OK && (*enrolled_password_handle == nullptr ||
+            *enrolled_password_handle_length != sizeof(password_handle_t))) {
+            ret = GATEKEEPER_RESPONSE_ERROR;
+            ALOGE("HAL: password_handle=%p size_of_handle=%" PRIu32 "\n",
+                  *enrolled_password_handle, *enrolled_password_handle_length);
+        }
+
+        if (ret == GATEKEEPER_RESPONSE_OK) {
             gatekeeper::password_handle_t *handle =
                     reinterpret_cast<gatekeeper::password_handle_t *>(*enrolled_password_handle);
             store_sid(uid, handle->user_id);
@@ -267,7 +274,7 @@
                         ret = rsp.timeout;
                     }
                 });
-                if (!hwRes.getStatus().isOk()) {
+                if (!hwRes.isOk()) {
                     ALOGE("verify transaction failed\n");
                     ret = -1;
                 }
@@ -297,9 +304,9 @@
             sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
             sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
             if (service != NULL) {
-                status_t ret = service->addAuthToken(*auth_token, *auth_token_length);
-                if (ret != ResponseCode::NO_ERROR) {
-                    ALOGE("Falure sending auth token to KeyStore: %d", ret);
+                auto ret = service->addAuthToken(*auth_token, *auth_token_length);
+                if (!ret.isOk()) {
+                    ALOGE("Failure sending auth token to KeyStore: %" PRId32, int32_t(ret));
                 }
             } else {
                 ALOGE("Unable to communicate with KeyStore");
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 0c90a54..4df0af1 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -347,6 +347,7 @@
 
 status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
     status_t ret = BAD_VALUE;
+    std::string buf;
 
     val->valueInt64 = LONG_MIN;
 
@@ -399,6 +400,15 @@
         }
         break;
 
+    case BATTERY_PROP_BATTERY_STATUS:
+        if (mAlwaysPluggedDevice) {
+            val->valueInt64 = BATTERY_STATUS_CHARGING;
+        } else {
+            val->valueInt64 = getChargeStatus();
+        }
+        ret = NO_ERROR;
+        break;
+
     default:
         break;
     }
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index cae6c4c..ed1971a 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -82,7 +82,7 @@
     // Initialize the board HAL - Equivalent of healthd_board_init(config)
     // in charger/recovery mode.
 
-    gHealth = IHealth::getService("health");
+    gHealth = IHealth::getService();
     if (gHealth == nullptr) {
         KLOG_WARNING(LOG_TAG, "unable to get HAL interface, using defaults\n");
         return;
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index ec3de34..49a534c 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -30,6 +30,8 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <functional>
+
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 
@@ -37,11 +39,12 @@
 #include <linux/netlink.h>
 
 #include <batteryservice/BatteryService.h>
-#include <cutils/android_reboot.h>
 #include <cutils/klog.h>
 #include <cutils/misc.h>
 #include <cutils/uevent.h>
 #include <cutils/properties.h>
+#include <minui/minui.h>
+#include <sys/reboot.h>
 
 #ifdef CHARGER_ENABLE_SUSPEND
 #include <suspend/autosuspend.h>
@@ -49,7 +52,6 @@
 
 #include "animation.h"
 #include "AnimationParser.h"
-#include "minui/minui.h"
 
 #include <healthd/healthd.h>
 
@@ -568,9 +570,8 @@
     }
 }
 
-static int set_key_callback(int code, int value, void *data)
+static int set_key_callback(struct charger *charger, int code, int value)
 {
-    struct charger *charger = (struct charger *)data;
     int64_t now = curr_time_ms();
     int down = !!value;
 
@@ -605,7 +606,7 @@
 {
     if (ev->type != EV_KEY)
         return;
-    set_key_callback(ev->code, ev->value, charger);
+    set_key_callback(charger, ev->code, ev->value);
 }
 
 static void set_next_key_check(struct charger *charger,
@@ -635,7 +636,7 @@
                 } else {
                     if (charger->batt_anim->cur_level >= charger->boot_min_cap) {
                         LOGW("[%" PRId64 "] rebooting\n", now);
-                        android_reboot(ANDROID_RB_RESTART, 0, 0);
+                        reboot(RB_AUTOBOOT);
                     } else {
                         LOGV("[%" PRId64 "] ignore power-button press, battery level "
                             "less than minimum\n", now);
@@ -690,7 +691,7 @@
                  now, (int64_t)UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
         } else if (now >= charger->next_pwr_check) {
             LOGW("[%" PRId64 "] shutting down\n", now);
-            android_reboot(ANDROID_RB_POWEROFF, 0, 0);
+            reboot(RB_POWER_OFF);
         } else {
             /* otherwise we already have a shutdown timer scheduled */
         }
@@ -762,9 +763,8 @@
    return (int)timeout;
 }
 
-static int input_callback(int fd, unsigned int epevents, void *data)
+static int input_callback(struct charger *charger, int fd, unsigned int epevents)
 {
-    struct charger *charger = (struct charger *)data;
     struct input_event ev;
     int ret;
 
@@ -841,7 +841,8 @@
 
     LOGW("--------------- STARTING CHARGER MODE ---------------\n");
 
-    ret = ev_init(input_callback, charger);
+    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);
@@ -880,7 +881,8 @@
             anim->frames[i].surface = scale_frames[i];
         }
     }
-    ev_sync_key_state(set_key_callback, charger);
+    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/android/log.h b/include/android/log.h
new file mode 120000
index 0000000..736c448
--- /dev/null
+++ b/include/android/log.h
@@ -0,0 +1 @@
+../../liblog/include/android/log.h
\ No newline at end of file
diff --git a/include/cutils b/include/cutils
new file mode 120000
index 0000000..ac2ed40
--- /dev/null
+++ b/include/cutils
@@ -0,0 +1 @@
+../libcutils/include/cutils/
\ No newline at end of file
diff --git a/include/cutils/debugger.h b/include/cutils/debugger.h
deleted file mode 100644
index 20e8796..0000000
--- a/include/cutils/debugger.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CUTILS_DEBUGGER_H
-#define __CUTILS_DEBUGGER_H
-
-#include <sys/cdefs.h>
-#include <sys/types.h>
-
-#include "debuggerd/client.h"
-
-__BEGIN_DECLS
-
-/* Dumps a process backtrace, registers, and stack to a tombstone file (requires root).
- * Stores the tombstone path in the provided buffer.
- * Returns 0 on success, -1 on error.
- */
-int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen);
-
-/* Dumps a process backtrace, registers, and stack to a tombstone file (requires root).
- * Stores the tombstone path in the provided buffer.
- * If reading debugger data from debuggerd ever takes longer than timeout_secs
- * seconds, then stop and return an error.
- * Returns 0 on success, -1 on error.
- */
-int dump_tombstone_timeout(pid_t tid, char* pathbuf, size_t pathlen, int timeout_secs);
-
-/* Dumps a process backtrace only to the specified file (requires root).
- * Returns 0 on success, -1 on error.
- */
-int dump_backtrace_to_file(pid_t tid, int fd);
-
-/* Dumps a process backtrace only to the specified file (requires root).
- * If reading debugger data from debuggerd ever takes longer than timeout_secs
- * seconds, then stop and return an error.
- * Returns 0 on success, -1 on error.
- */
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
-
-__END_DECLS
-
-#endif /* __CUTILS_DEBUGGER_H */
diff --git a/include/log b/include/log
new file mode 120000
index 0000000..714065f
--- /dev/null
+++ b/include/log
@@ -0,0 +1 @@
+../liblog/include/log
\ No newline at end of file
diff --git a/include/log/log.h b/include/log/log.h
deleted file mode 100644
index d6f0eb5..0000000
--- a/include/log/log.h
+++ /dev/null
@@ -1,823 +0,0 @@
-/*
- * Copyright (C) 2005-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 _LIBS_LOG_LOG_H
-#define _LIBS_LOG_LOG_H
-
-/* Too many in the ecosystem assume these are included */
-#if !defined(_WIN32)
-#include <pthread.h>
-#endif
-#include <stdint.h>  /* uint16_t, int32_t */
-#include <stdio.h>
-#include <sys/types.h>
-#include <time.h>    /* clock_gettime */
-#include <unistd.h>
-
-#include <android/log.h>
-#include <log/uio.h> /* helper to define iovec for portability */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * LOG_TAG is the local tag used for the following simplified
- * logging macros.  You can change this preprocessor definition
- * before using the other macros to change the tag.
- */
-
-#ifndef LOG_TAG
-#define LOG_TAG NULL
-#endif
-
-/* --------------------------------------------------------------------- */
-
-/*
- * This file uses ", ## __VA_ARGS__" zero-argument token pasting to
- * work around issues with debug-only syntax errors in assertions
- * that are missing format strings.  See commit
- * 19299904343daf191267564fe32e6cd5c165cd42
- */
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
-#endif
-
-/*
- * Send a simple string to the log.
- */
-int __android_log_buf_write(int bufID, int prio, const char* tag, const char* text);
-int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
-#if defined(__GNUC__)
-    __attribute__((__format__(printf, 4, 5)))
-#endif
-    ;
-
-/*
- * Simplified macro to send a verbose system log message using current LOG_TAG.
- */
-#ifndef SLOGV
-#define __SLOGV(...) \
-    ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
-#if LOG_NDEBUG
-#define SLOGV(...) do { if (0) { __SLOGV(__VA_ARGS__); } } while (0)
-#else
-#define SLOGV(...) __SLOGV(__VA_ARGS__)
-#endif
-#endif
-
-#ifndef SLOGV_IF
-#if LOG_NDEBUG
-#define SLOGV_IF(cond, ...)   ((void)0)
-#else
-#define SLOGV_IF(cond, ...) \
-    ( (__predict_false(cond)) \
-    ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
-    : (void)0 )
-#endif
-#endif
-
-/*
- * Simplified macro to send a debug system log message using current LOG_TAG.
- */
-#ifndef SLOGD
-#define SLOGD(...) \
-    ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef SLOGD_IF
-#define SLOGD_IF(cond, ...) \
-    ( (__predict_false(cond)) \
-    ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
-    : (void)0 )
-#endif
-
-/*
- * Simplified macro to send an info system log message using current LOG_TAG.
- */
-#ifndef SLOGI
-#define SLOGI(...) \
-    ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef SLOGI_IF
-#define SLOGI_IF(cond, ...) \
-    ( (__predict_false(cond)) \
-    ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
-    : (void)0 )
-#endif
-
-/*
- * Simplified macro to send a warning system log message using current LOG_TAG.
- */
-#ifndef SLOGW
-#define SLOGW(...) \
-    ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef SLOGW_IF
-#define SLOGW_IF(cond, ...) \
-    ( (__predict_false(cond)) \
-    ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
-    : (void)0 )
-#endif
-
-/*
- * Simplified macro to send an error system log message using current LOG_TAG.
- */
-#ifndef SLOGE
-#define SLOGE(...) \
-    ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef SLOGE_IF
-#define SLOGE_IF(cond, ...) \
-    ( (__predict_false(cond)) \
-    ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
-    : (void)0 )
-#endif
-
-/* --------------------------------------------------------------------- */
-
-/*
- * Simplified macro to send a verbose radio log message using current LOG_TAG.
- */
-#ifndef RLOGV
-#define __RLOGV(...) \
-    ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
-#if LOG_NDEBUG
-#define RLOGV(...) do { if (0) { __RLOGV(__VA_ARGS__); } } while (0)
-#else
-#define RLOGV(...) __RLOGV(__VA_ARGS__)
-#endif
-#endif
-
-#ifndef RLOGV_IF
-#if LOG_NDEBUG
-#define RLOGV_IF(cond, ...)   ((void)0)
-#else
-#define RLOGV_IF(cond, ...) \
-    ( (__predict_false(cond)) \
-    ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
-    : (void)0 )
-#endif
-#endif
-
-/*
- * Simplified macro to send a debug radio log message using  current LOG_TAG.
- */
-#ifndef RLOGD
-#define RLOGD(...) \
-    ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef RLOGD_IF
-#define RLOGD_IF(cond, ...) \
-    ( (__predict_false(cond)) \
-    ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
-    : (void)0 )
-#endif
-
-/*
- * Simplified macro to send an info radio log message using  current LOG_TAG.
- */
-#ifndef RLOGI
-#define RLOGI(...) \
-    ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef RLOGI_IF
-#define RLOGI_IF(cond, ...) \
-    ( (__predict_false(cond)) \
-    ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
-    : (void)0 )
-#endif
-
-/*
- * Simplified macro to send a warning radio log message using current LOG_TAG.
- */
-#ifndef RLOGW
-#define RLOGW(...) \
-    ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef RLOGW_IF
-#define RLOGW_IF(cond, ...) \
-    ( (__predict_false(cond)) \
-    ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
-    : (void)0 )
-#endif
-
-/*
- * Simplified macro to send an error radio log message using current LOG_TAG.
- */
-#ifndef RLOGE
-#define RLOGE(...) \
-    ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef RLOGE_IF
-#define RLOGE_IF(cond, ...) \
-    ( (__predict_false(cond)) \
-    ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
-    : (void)0 )
-#endif
-
-/* --------------------------------------------------------------------- */
-
-/*
- * Event logging.
- */
-
-/*
- * The following should not be used directly.
- */
-
-int __android_log_bwrite(int32_t tag, const void* payload, size_t len);
-int __android_log_btwrite(int32_t tag, char type, const void* payload,
-                          size_t len);
-int __android_log_bswrite(int32_t tag, const char* payload);
-
-#define android_bWriteLog(tag, payload, len) \
-    __android_log_bwrite(tag, payload, len)
-#define android_btWriteLog(tag, type, payload, len) \
-    __android_log_btwrite(tag, type, payload, len)
-
-/*
- * Event log entry types.
- */
-#ifndef __AndroidEventLogType_defined
-#define __AndroidEventLogType_defined
-typedef enum {
-    /* Special markers for android_log_list_element type */
-    EVENT_TYPE_LIST_STOP = '\n', /* declare end of list  */
-    EVENT_TYPE_UNKNOWN   = '?',  /* protocol error       */
-
-    /* must match with declaration in java/android/android/util/EventLog.java */
-    EVENT_TYPE_INT       = 0,    /* int32_t */
-    EVENT_TYPE_LONG      = 1,    /* int64_t */
-    EVENT_TYPE_STRING    = 2,
-    EVENT_TYPE_LIST      = 3,
-    EVENT_TYPE_FLOAT     = 4,
-} AndroidEventLogType;
-#endif
-#define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType)
-#define typeof_AndroidEventLogType unsigned char
-
-#ifndef LOG_EVENT_INT
-#define LOG_EVENT_INT(_tag, _value) {                                       \
-        int intBuf = _value;                                                \
-        (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf,            \
-            sizeof(intBuf));                                                \
-    }
-#endif
-#ifndef LOG_EVENT_LONG
-#define LOG_EVENT_LONG(_tag, _value) {                                      \
-        long long longBuf = _value;                                         \
-        (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf,          \
-            sizeof(longBuf));                                               \
-    }
-#endif
-#ifndef LOG_EVENT_FLOAT
-#define LOG_EVENT_FLOAT(_tag, _value) {                                     \
-        float floatBuf = _value;                                            \
-        (void) android_btWriteLog(_tag, EVENT_TYPE_FLOAT, &floatBuf,        \
-            sizeof(floatBuf));                                              \
-    }
-#endif
-#ifndef LOG_EVENT_STRING
-#define LOG_EVENT_STRING(_tag, _value)                                      \
-        (void) __android_log_bswrite(_tag, _value);
-#endif
-
-#ifndef log_id_t_defined
-#define log_id_t_defined
-typedef enum log_id {
-    LOG_ID_MIN = 0,
-
-    LOG_ID_MAIN = 0,
-    LOG_ID_RADIO = 1,
-    LOG_ID_EVENTS = 2,
-    LOG_ID_SYSTEM = 3,
-    LOG_ID_CRASH = 4,
-    LOG_ID_SECURITY = 5,
-    LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */
-
-    LOG_ID_MAX
-} log_id_t;
-#endif
-#define sizeof_log_id_t sizeof(typeof_log_id_t)
-#define typeof_log_id_t unsigned char
-
-/* --------------------------------------------------------------------- */
-
-/*
- * Native log reading interface section. See logcat for sample code.
- *
- * The preferred API is an exec of logcat. Likely uses of this interface
- * are if native code suffers from exec or filtration being too costly,
- * access to raw information, or parsing is an issue.
- */
-
-/*
- * The userspace structure for version 1 of the logger_entry ABI.
- */
-#ifndef __struct_logger_entry_defined
-#define __struct_logger_entry_defined
-struct logger_entry {
-    uint16_t    len;    /* length of the payload */
-    uint16_t    __pad;  /* no matter what, we get 2 bytes of padding */
-    int32_t     pid;    /* generating process's pid */
-    int32_t     tid;    /* generating process's tid */
-    int32_t     sec;    /* seconds since Epoch */
-    int32_t     nsec;   /* nanoseconds */
-#ifndef __cplusplus
-    char        msg[0]; /* the entry's payload */
-#endif
-};
-#endif
-
-/*
- * The userspace structure for version 2 of the logger_entry ABI.
- */
-#ifndef __struct_logger_entry_v2_defined
-#define __struct_logger_entry_v2_defined
-struct logger_entry_v2 {
-    uint16_t    len;       /* length of the payload */
-    uint16_t    hdr_size;  /* sizeof(struct logger_entry_v2) */
-    int32_t     pid;       /* generating process's pid */
-    int32_t     tid;       /* generating process's tid */
-    int32_t     sec;       /* seconds since Epoch */
-    int32_t     nsec;      /* nanoseconds */
-    uint32_t    euid;      /* effective UID of logger */
-#ifndef __cplusplus
-    char        msg[0];    /* the entry's payload */
-#endif
-} __attribute__((__packed__));
-#endif
-
-/*
- * The userspace structure for version 3 of the logger_entry ABI.
- */
-#ifndef __struct_logger_entry_v3_defined
-#define __struct_logger_entry_v3_defined
-struct logger_entry_v3 {
-    uint16_t    len;       /* length of the payload */
-    uint16_t    hdr_size;  /* sizeof(struct logger_entry_v3) */
-    int32_t     pid;       /* generating process's pid */
-    int32_t     tid;       /* generating process's tid */
-    int32_t     sec;       /* seconds since Epoch */
-    int32_t     nsec;      /* nanoseconds */
-    uint32_t    lid;       /* log id of the payload */
-#ifndef __cplusplus
-    char        msg[0];    /* the entry's payload */
-#endif
-} __attribute__((__packed__));
-#endif
-
-/*
- * The userspace structure for version 4 of the logger_entry ABI.
- */
-#ifndef __struct_logger_entry_v4_defined
-#define __struct_logger_entry_v4_defined
-struct logger_entry_v4 {
-    uint16_t    len;       /* length of the payload */
-    uint16_t    hdr_size;  /* sizeof(struct logger_entry_v4) */
-    int32_t     pid;       /* generating process's pid */
-    uint32_t    tid;       /* generating process's tid */
-    uint32_t    sec;       /* seconds since Epoch */
-    uint32_t    nsec;      /* nanoseconds */
-    uint32_t    lid;       /* log id of the payload, bottom 4 bits currently */
-    uint32_t    uid;       /* generating process's uid */
-#ifndef __cplusplus
-    char        msg[0];    /* the entry's payload */
-#endif
-};
-#endif
-
-/* struct log_time is a wire-format variant of struct timespec */
-#define NS_PER_SEC 1000000000ULL
-
-#ifndef __struct_log_time_defined
-#define __struct_log_time_defined
-#ifdef __cplusplus
-
-/*
- * NB: we did NOT define a copy constructor. This will result in structure
- * no longer being compatible with pass-by-value which is desired
- * efficient behavior. Also, pass-by-reference breaks C/C++ ABI.
- */
-struct log_time {
-public:
-    uint32_t tv_sec; /* good to Feb 5 2106 */
-    uint32_t tv_nsec;
-
-    static const uint32_t tv_sec_max = 0xFFFFFFFFUL;
-    static const uint32_t tv_nsec_max = 999999999UL;
-
-    log_time(const timespec& T)
-    {
-        tv_sec = static_cast<uint32_t>(T.tv_sec);
-        tv_nsec = static_cast<uint32_t>(T.tv_nsec);
-    }
-    log_time(uint32_t sec, uint32_t nsec)
-    {
-        tv_sec = sec;
-        tv_nsec = nsec;
-    }
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
-#define __struct_log_time_private_defined
-    static const timespec EPOCH;
-#endif
-    log_time()
-    {
-    }
-#ifdef __linux__
-    log_time(clockid_t id)
-    {
-        timespec T;
-        clock_gettime(id, &T);
-        tv_sec = static_cast<uint32_t>(T.tv_sec);
-        tv_nsec = static_cast<uint32_t>(T.tv_nsec);
-    }
-#endif
-    log_time(const char* T)
-    {
-        const uint8_t* c = reinterpret_cast<const uint8_t*>(T);
-        tv_sec = c[0] |
-                 (static_cast<uint32_t>(c[1]) << 8) |
-                 (static_cast<uint32_t>(c[2]) << 16) |
-                 (static_cast<uint32_t>(c[3]) << 24);
-        tv_nsec = c[4] |
-                  (static_cast<uint32_t>(c[5]) << 8) |
-                  (static_cast<uint32_t>(c[6]) << 16) |
-                  (static_cast<uint32_t>(c[7]) << 24);
-    }
-
-    /* timespec */
-    bool operator== (const timespec& T) const
-    {
-        return (tv_sec == static_cast<uint32_t>(T.tv_sec))
-            && (tv_nsec == static_cast<uint32_t>(T.tv_nsec));
-    }
-    bool operator!= (const timespec& T) const
-    {
-        return !(*this == T);
-    }
-    bool operator< (const timespec& T) const
-    {
-        return (tv_sec < static_cast<uint32_t>(T.tv_sec))
-            || ((tv_sec == static_cast<uint32_t>(T.tv_sec))
-                && (tv_nsec < static_cast<uint32_t>(T.tv_nsec)));
-    }
-    bool operator>= (const timespec& T) const
-    {
-        return !(*this < T);
-    }
-    bool operator> (const timespec& T) const
-    {
-        return (tv_sec > static_cast<uint32_t>(T.tv_sec))
-            || ((tv_sec == static_cast<uint32_t>(T.tv_sec))
-                && (tv_nsec > static_cast<uint32_t>(T.tv_nsec)));
-    }
-    bool operator<= (const timespec& T) const
-    {
-        return !(*this > T);
-    }
-
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
-    log_time operator-= (const timespec& T);
-    log_time operator- (const timespec& T) const
-    {
-        log_time local(*this);
-        return local -= T;
-    }
-    log_time operator+= (const timespec& T);
-    log_time operator+ (const timespec& T) const
-    {
-        log_time local(*this);
-        return local += T;
-    }
-#endif
-
-    /* log_time */
-    bool operator== (const log_time& T) const
-    {
-        return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec);
-    }
-    bool operator!= (const log_time& T) const
-    {
-        return !(*this == T);
-    }
-    bool operator< (const log_time& T) const
-    {
-        return (tv_sec < T.tv_sec)
-            || ((tv_sec == T.tv_sec) && (tv_nsec < T.tv_nsec));
-    }
-    bool operator>= (const log_time& T) const
-    {
-        return !(*this < T);
-    }
-    bool operator> (const log_time& T) const
-    {
-        return (tv_sec > T.tv_sec)
-            || ((tv_sec == T.tv_sec) && (tv_nsec > T.tv_nsec));
-    }
-    bool operator<= (const log_time& T) const
-    {
-        return !(*this > T);
-    }
-
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
-    log_time operator-= (const log_time& T);
-    log_time operator- (const log_time& T) const
-    {
-        log_time local(*this);
-        return local -= T;
-    }
-    log_time operator+= (const log_time& T);
-    log_time operator+ (const log_time& T) const
-    {
-        log_time local(*this);
-        return local += T;
-    }
-#endif
-
-    uint64_t nsec() const
-    {
-        return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
-    }
-
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
-    static const char default_format[];
-
-    /* Add %#q for the fraction of a second to the standard library functions */
-    char* strptime(const char* s, const char* format = default_format);
-#endif
-} __attribute__((__packed__));
-
-#else
-
-typedef struct log_time {
-    uint32_t tv_sec;
-    uint32_t tv_nsec;
-} __attribute__((__packed__)) log_time;
-
-#endif
-#endif
-
-/*
- * The maximum size of the log entry payload that can be
- * written to the logger. An attempt to write more than
- * this amount will result in a truncated log entry.
- */
-#define LOGGER_ENTRY_MAX_PAYLOAD 4068
-
-/*
- * The maximum size of a log entry which can be read from the
- * kernel logger driver. An attempt to read less than this amount
- * may result in read() returning EINVAL.
- */
-#define LOGGER_ENTRY_MAX_LEN    (5*1024)
-
-#ifndef __struct_log_msg_defined
-#define __struct_log_msg_defined
-struct log_msg {
-    union {
-        unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
-        struct logger_entry_v4 entry;
-        struct logger_entry_v4 entry_v4;
-        struct logger_entry_v3 entry_v3;
-        struct logger_entry_v2 entry_v2;
-        struct logger_entry    entry_v1;
-    } __attribute__((aligned(4)));
-#ifdef __cplusplus
-    /* Matching log_time operators */
-    bool operator== (const log_msg& T) const
-    {
-        return (entry.sec == T.entry.sec) && (entry.nsec == T.entry.nsec);
-    }
-    bool operator!= (const log_msg& T) const
-    {
-        return !(*this == T);
-    }
-    bool operator< (const log_msg& T) const
-    {
-        return (entry.sec < T.entry.sec)
-            || ((entry.sec == T.entry.sec)
-             && (entry.nsec < T.entry.nsec));
-    }
-    bool operator>= (const log_msg& T) const
-    {
-        return !(*this < T);
-    }
-    bool operator> (const log_msg& T) const
-    {
-        return (entry.sec > T.entry.sec)
-            || ((entry.sec == T.entry.sec)
-             && (entry.nsec > T.entry.nsec));
-    }
-    bool operator<= (const log_msg& T) const
-    {
-        return !(*this > T);
-    }
-    uint64_t nsec() const
-    {
-        return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec;
-    }
-
-    /* packet methods */
-    log_id_t id()
-    {
-        return static_cast<log_id_t>(entry.lid);
-    }
-    char* msg()
-    {
-        unsigned short hdr_size = entry.hdr_size;
-        if (!hdr_size) {
-            hdr_size = sizeof(entry_v1);
-        }
-        if ((hdr_size < sizeof(entry_v1)) || (hdr_size > sizeof(entry))) {
-            return NULL;
-        }
-        return reinterpret_cast<char*>(buf) + hdr_size;
-    }
-    unsigned int len()
-    {
-        return (entry.hdr_size ?
-                    entry.hdr_size :
-                    static_cast<uint16_t>(sizeof(entry_v1))) +
-               entry.len;
-    }
-#endif
-};
-#endif
-
-#ifndef __ANDROID_USE_LIBLOG_READER_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 3
-#elif __ANDROID_API__ > 23 /* > Marshmallow */
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 3
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 2
-#elif __ANDROID_API__ > 19 /* > KitKat */
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_READER_INTERFACE
-
-struct logger;
-
-log_id_t android_logger_get_id(struct logger* logger);
-
-int android_logger_clear(struct logger* logger);
-long android_logger_get_log_size(struct logger* logger);
-int android_logger_set_log_size(struct logger* logger, unsigned long size);
-long android_logger_get_log_readable_size(struct logger* logger);
-int android_logger_get_log_version(struct logger* logger);
-
-struct logger_list;
-
-#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 1
-ssize_t android_logger_get_statistics(struct logger_list* logger_list,
-                                      char* buf, size_t len);
-ssize_t android_logger_get_prune_list(struct logger_list* logger_list,
-                                      char* buf, size_t len);
-int android_logger_set_prune_list(struct logger_list* logger_list,
-                                  char* buf, size_t len);
-#endif
-
-#define ANDROID_LOG_RDONLY   O_RDONLY
-#define ANDROID_LOG_WRONLY   O_WRONLY
-#define ANDROID_LOG_RDWR     O_RDWR
-#define ANDROID_LOG_ACCMODE  O_ACCMODE
-#define ANDROID_LOG_NONBLOCK O_NONBLOCK
-#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 2
-#define ANDROID_LOG_WRAP     0x40000000 /* Block until buffer about to wrap */
-#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
-#endif
-#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 1
-#define ANDROID_LOG_PSTORE   0x80000000
-#endif
-
-struct logger_list* android_logger_list_alloc(int mode,
-                                              unsigned int tail,
-                                              pid_t pid);
-struct logger_list* android_logger_list_alloc_time(int mode,
-                                                   log_time start,
-                                                   pid_t pid);
-void android_logger_list_free(struct logger_list* logger_list);
-/* In the purest sense, the following two are orthogonal interfaces */
-int android_logger_list_read(struct logger_list* logger_list,
-                             struct log_msg* log_msg);
-
-/* Multiple log_id_t opens */
-struct logger* android_logger_open(struct logger_list* logger_list,
-                                   log_id_t id);
-#define android_logger_close android_logger_free
-/* Single log_id_t open */
-struct logger_list* android_logger_list_open(log_id_t id,
-                                             int mode,
-                                             unsigned int tail,
-                                             pid_t pid);
-#define android_logger_list_close android_logger_list_free
-
-#endif /* __ANDROID_USE_LIBLOG_READER_INTERFACE */
-
-#ifdef __linux__
-
-#ifndef __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 1
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
-clockid_t android_log_clockid();
-#endif
-
-#endif /* __linux__ */
-
-/*
- * log_id_t helpers
- */
-log_id_t android_name_to_log_id(const char* logName);
-const char* android_log_id_to_name(log_id_t log_id);
-
-/* --------------------------------------------------------------------- */
-
-#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
-#elif __ANDROID_API__ > 18 /* > JellyBean */
-#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_CLOSE_INTERFACE
-/*
- * Release any logger resources (a new log write will immediately re-acquire)
- *
- * May be used to clean up File descriptors after a Fork, the resources are
- * all O_CLOEXEC so wil self clean on exec().
- */
-void __android_log_close();
-#endif
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBS_LOG_LOG_H */
diff --git a/include/log/logd.h b/include/log/logd.h
deleted file mode 100644
index 0e0248e..0000000
--- a/include/log/logd.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <log/log.h>
diff --git a/include/log/logger.h b/include/log/logger.h
deleted file mode 100644
index 0e0248e..0000000
--- a/include/log/logger.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <log/log.h>
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index c9e1923..8e2bc1c 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -19,6 +19,33 @@
 ** by the device side of adb.
 */
 
+/*
+ * This file is consumed by build/tools/fs_config and is used
+ * for generating various files. Anything #define AID_<name>
+ * becomes the mapping for getpwnam/getpwuid, etc. The <name>
+ * field is lowercased.
+ * For example:
+ * #define AID_FOO_BAR 6666 becomes a friendly name of "foo_bar"
+ *
+ * The above holds true with the exception of:
+ *   mediacodec
+ *   mediaex
+ *   mediadrm
+ * Whose friendly names do not match the #define statements.
+ *
+ * Additionally, AID_OEM_RESERVED_START and AID_OEM_RESERVED_END
+ * can be used to define reserved OEM ranges used for sanity checks
+ * during the build process. The rules are, they must end with START/END
+ * The proper convention is incrementing a number like so:
+ * AID_OEM_RESERVED_START
+ * AID_OEM_RESERVED_1_START
+ * AID_OEM_RESERVED_2_START
+ * ...
+ * The same applies to the END.
+ * They are not required to be in order, but must not overlap each other and
+ * must define a START and END'ing range. START must be smaller than END.
+ */
+
 #ifndef _ANDROID_FILESYSTEM_CONFIG_H_
 #define _ANDROID_FILESYSTEM_CONFIG_H_
 
@@ -99,6 +126,8 @@
 #define AID_MEDIA_AUDIO   1055 /* GID for audio files on internal media storage */
 #define AID_MEDIA_VIDEO   1056 /* GID for video files on internal media storage */
 #define AID_MEDIA_IMAGE   1057 /* GID for image files on internal media storage */
+#define AID_TOMBSTONED    1058  /* tombstoned user */
+#define AID_MEDIA_OBB     1059 /* GID for OBB files on internal media storage */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL         2000  /* adb and debug shell user */
@@ -130,114 +159,34 @@
 #define AID_MISC          9998  /* access to misc storage */
 #define AID_NOBODY        9999
 
-#define AID_APP          10000  /* first app user */
+#define AID_APP              10000 /* TODO: switch users over to AID_APP_START */
+#define AID_APP_START        10000 /* first app user */
+#define AID_APP_END          19999 /* last app user */
 
-#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
-#define AID_ISOLATED_END   99999 /* end of uids for fully isolated sandboxed processes */
+#define AID_CACHE_GID_START  20000 /* start of gids for apps to mark cached data */
+#define AID_CACHE_GID_END    29999 /* end of gids for apps to mark cached data */
 
-#define AID_USER        100000  /* offset for uid ranges for each user */
+#define AID_EXT_GID_START    30000 /* start of gids for apps to mark external data */
+#define AID_EXT_GID_END      39999 /* end of gids for apps to mark external data */
 
 #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
-#define AID_SHARED_GID_END   59999 /* start of gids for apps in each user to share */
+#define AID_SHARED_GID_END   59999 /* end of gids for apps in each user to share */
+
+#define AID_ISOLATED_START   99000 /* start of uids for fully isolated sandboxed processes */
+#define AID_ISOLATED_END     99999 /* end of uids for fully isolated sandboxed processes */
+
+#define AID_USER            100000 /* TODO: switch users over to AID_USER_OFFSET */
+#define AID_USER_OFFSET     100000 /* offset for uid ranges for each user */
+
+/*
+ * android_ids has moved to pwd/grp functionality.
+ * If you need to add one, the structure is now
+ * auto-generated based on the AID_ constraints
+ * documented at the top of this header file.
+ * Also see build/tools/fs_config for more details.
+ */
 
 #if !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
-/*
- * Used in:
- *  bionic/libc/bionic/stubs.cpp
- *  external/libselinux/src/android.c
- *  system/core/logd/LogStatistics.cpp
- *  system/core/init/ueventd.cpp
- *  system/core/init/util.cpp
- */
-struct android_id_info {
-    const char *name;
-    unsigned aid;
-};
-
-static const struct android_id_info android_ids[] = {
-    { "root",          AID_ROOT, },
-
-    { "system",        AID_SYSTEM, },
-
-    { "radio",         AID_RADIO, },
-    { "bluetooth",     AID_BLUETOOTH, },
-    { "graphics",      AID_GRAPHICS, },
-    { "input",         AID_INPUT, },
-    { "audio",         AID_AUDIO, },
-    { "camera",        AID_CAMERA, },
-    { "log",           AID_LOG, },
-    { "compass",       AID_COMPASS, },
-    { "mount",         AID_MOUNT, },
-    { "wifi",          AID_WIFI, },
-    { "adb",           AID_ADB, },
-    { "install",       AID_INSTALL, },
-    { "media",         AID_MEDIA, },
-    { "dhcp",          AID_DHCP, },
-    { "sdcard_rw",     AID_SDCARD_RW, },
-    { "vpn",           AID_VPN, },
-    { "keystore",      AID_KEYSTORE, },
-    { "usb",           AID_USB, },
-    { "drm",           AID_DRM, },
-    { "mdnsr",         AID_MDNSR, },
-    { "gps",           AID_GPS, },
-    // AID_UNUSED1
-    { "media_rw",      AID_MEDIA_RW, },
-    { "mtp",           AID_MTP, },
-    // AID_UNUSED2
-    { "drmrpc",        AID_DRMRPC, },
-    { "nfc",           AID_NFC, },
-    { "sdcard_r",      AID_SDCARD_R, },
-    { "clat",          AID_CLAT, },
-    { "loop_radio",    AID_LOOP_RADIO, },
-    { "mediadrm",      AID_MEDIA_DRM, },
-    { "package_info",  AID_PACKAGE_INFO, },
-    { "sdcard_pics",   AID_SDCARD_PICS, },
-    { "sdcard_av",     AID_SDCARD_AV, },
-    { "sdcard_all",    AID_SDCARD_ALL, },
-    { "logd",          AID_LOGD, },
-    { "shared_relro",  AID_SHARED_RELRO, },
-    { "dbus",          AID_DBUS, },
-    { "tlsdate",       AID_TLSDATE, },
-    { "mediaex",       AID_MEDIA_EX, },
-    { "audioserver",   AID_AUDIOSERVER, },
-    { "metrics_coll",  AID_METRICS_COLL },
-    { "metricsd",      AID_METRICSD },
-    { "webserv",       AID_WEBSERV },
-    { "debuggerd",     AID_DEBUGGERD, },
-    { "mediacodec",    AID_MEDIA_CODEC, },
-    { "cameraserver",  AID_CAMERASERVER, },
-    { "firewall",      AID_FIREWALL, },
-    { "trunks",        AID_TRUNKS, },
-    { "nvram",         AID_NVRAM, },
-    { "dns",           AID_DNS, },
-    { "dns_tether",    AID_DNS_TETHER, },
-    { "webview_zygote", AID_WEBVIEW_ZYGOTE, },
-    { "vehicle_network", AID_VEHICLE_NETWORK, },
-    { "media_audio",   AID_MEDIA_AUDIO, },
-    { "media_video",   AID_MEDIA_VIDEO, },
-    { "media_image",   AID_MEDIA_IMAGE, },
-
-    { "shell",         AID_SHELL, },
-    { "cache",         AID_CACHE, },
-    { "diag",          AID_DIAG, },
-
-    { "net_bt_admin",  AID_NET_BT_ADMIN, },
-    { "net_bt",        AID_NET_BT, },
-    { "inet",          AID_INET, },
-    { "net_raw",       AID_NET_RAW, },
-    { "net_admin",     AID_NET_ADMIN, },
-    { "net_bw_stats",  AID_NET_BW_STATS, },
-    { "net_bw_acct",   AID_NET_BW_ACCT, },
-    { "readproc",      AID_READPROC, },
-    { "wakelock",      AID_WAKELOCK, },
-
-    { "everybody",     AID_EVERYBODY, },
-    { "misc",          AID_MISC, },
-    { "nobody",        AID_NOBODY, },
-};
-
-#define android_id_count \
-    (sizeof(android_ids) / sizeof(android_ids[0]))
 
 struct fs_path_config {
     unsigned mode;
diff --git a/include/private/android_logger.h b/include/private/android_logger.h
new file mode 120000
index 0000000..f187a6d
--- /dev/null
+++ b/include/private/android_logger.h
@@ -0,0 +1 @@
+../../liblog/include/private/android_logger.h
\ No newline at end of file
diff --git a/include/system/graphics-base.h b/include/system/graphics-base.h
index b86d031..2a44faf 100644
--- a/include/system/graphics-base.h
+++ b/include/system/graphics-base.h
@@ -1,7 +1,9 @@
 // This file is autogenerated by hidl-gen. Do not edit manually.
+// Source: android.hardware.graphics.common@1.0
+// Root: android.hardware:hardware/interfaces
 
-#ifndef HIDL_GENERATED_android_hardware_graphics_common_V1_0_EXPORTED_CONSTANTS_H_
-#define HIDL_GENERATED_android_hardware_graphics_common_V1_0_EXPORTED_CONSTANTS_H_
+#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
+#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
 
 #ifdef __cplusplus
 extern "C" {
@@ -13,8 +15,8 @@
     HAL_PIXEL_FORMAT_RGB_888 = 3,
     HAL_PIXEL_FORMAT_RGB_565 = 4,
     HAL_PIXEL_FORMAT_BGRA_8888 = 5,
-    HAL_PIXEL_FORMAT_RGBA_FP16 = 16, // 0x10
-    HAL_PIXEL_FORMAT_RGBX_FP16 = 17, // 0x11
+    HAL_PIXEL_FORMAT_RGBA_1010102 = 43, // 0x2B
+    HAL_PIXEL_FORMAT_RGBA_FP16 = 22, // 0x16
     HAL_PIXEL_FORMAT_YV12 = 842094169, // 0x32315659
     HAL_PIXEL_FORMAT_Y8 = 538982489, // 0x20203859
     HAL_PIXEL_FORMAT_Y16 = 540422489, // 0x20363159
@@ -32,6 +34,7 @@
     HAL_PIXEL_FORMAT_YCBCR_422_SP = 16, // 0x10
     HAL_PIXEL_FORMAT_YCRCB_420_SP = 17, // 0x11
     HAL_PIXEL_FORMAT_YCBCR_422_I = 20, // 0x14
+    HAL_PIXEL_FORMAT_JPEG = 256, // 0x100
 } android_pixel_format_t;
 
 typedef enum {
@@ -92,10 +95,14 @@
     HAL_DATASPACE_V0_BT709 = 281083904, // ((STANDARD_BT709 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
     HAL_DATASPACE_DCI_P3_LINEAR = 139067392, // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
     HAL_DATASPACE_DCI_P3 = 155844608, // ((STANDARD_DCI_P3 | TRANSFER_GAMMA2_6) | RANGE_FULL)
+    HAL_DATASPACE_DISPLAY_P3_LINEAR = 139067392, // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
+    HAL_DATASPACE_DISPLAY_P3 = 143261696, // ((STANDARD_DCI_P3 | TRANSFER_SRGB) | RANGE_FULL)
     HAL_DATASPACE_ADOBE_RGB = 151715840, // ((STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2) | RANGE_FULL)
     HAL_DATASPACE_BT2020_LINEAR = 138805248, // ((STANDARD_BT2020 | TRANSFER_LINEAR) | RANGE_FULL)
     HAL_DATASPACE_BT2020 = 147193856, // ((STANDARD_BT2020 | TRANSFER_SMPTE_170M) | RANGE_FULL)
+    HAL_DATASPACE_BT2020_PQ = 163971072, // ((STANDARD_BT2020 | TRANSFER_ST2084) | RANGE_FULL)
     HAL_DATASPACE_DEPTH = 4096, // 0x1000
+    HAL_DATASPACE_SENSOR = 4097, // 0x1001
 } android_dataspace_t;
 
 typedef enum {
@@ -108,6 +115,7 @@
     HAL_COLOR_MODE_DCI_P3 = 6,
     HAL_COLOR_MODE_SRGB = 7,
     HAL_COLOR_MODE_ADOBE_RGB = 8,
+    HAL_COLOR_MODE_DISPLAY_P3 = 9,
 } android_color_mode_t;
 
 typedef enum {
@@ -130,4 +138,4 @@
 }
 #endif
 
-#endif  // HIDL_GENERATED_android_hardware_graphics_common_V1_0_EXPORTED_CONSTANTS_H_
+#endif  // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
diff --git a/include/system/graphics.h b/include/system/graphics.h
index 449b8c7..1a99187 100644
--- a/include/system/graphics.h
+++ b/include/system/graphics.h
@@ -237,6 +237,26 @@
 #endif
 };
 
+/**
+  * These structures are used to define the reference display's
+  * capabilities for HDR content. Display engine can use this
+  * to better tone map content to user's display.
+  * Color is defined in CIE XYZ coordinates
+  */
+struct android_xy_color {
+    float x;
+    float y;
+};
+
+struct android_smpte2086_metadata {
+    struct android_xy_color displayPrimaryRed;
+    struct android_xy_color displayPrimaryGreen;
+    struct android_xy_color displayPrimaryBlue;
+    struct android_xy_color whitePoint;
+    float maxLuminance;
+    float minLuminance;
+};
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/system/radio.h b/include/system/radio.h
index b164fc8..acf3ea7 100644
--- a/include/system/radio.h
+++ b/include/system/radio.h
@@ -84,7 +84,7 @@
 typedef uint32_t radio_handle_t;
 
 /* Opaque meta data structure used by radio meta data API (see system/radio_metadata.h) */
-typedef struct radio_medtadata radio_metadata_t;
+typedef struct radio_metadata radio_metadata_t;
 
 
 /* Additional attributes for an FM band configuration */
@@ -170,7 +170,7 @@
     bool             stereo;    /* program is stereo or not */
     bool             digital;   /* digital program or not (e.g HD Radio program) */
     uint32_t         signal_strength; /* signal strength from 0 to 100 */
-                                /* non null if meta data are present (e.g PTY, song title ...) */
+                                /* meta data (e.g PTY, song title ...), must not be NULL */
     __attribute__((aligned(8))) radio_metadata_t *metadata;
 } radio_program_info_t;
 
@@ -214,7 +214,7 @@
         bool                    on;
         radio_band_config_t     config;
         radio_program_info_t    info;
-                                /* non null if meta data are present (e.g PTY, song title ...) */
+                                /* meta data (e.g PTY, song title ...), must not be NULL */
         __attribute__((aligned(8))) radio_metadata_t *metadata;
     };
 } radio_event_t;
diff --git a/include/system/window.h b/include/system/window.h
index abd3282..8149ca5 100644
--- a/include/system/window.h
+++ b/include/system/window.h
@@ -330,6 +330,7 @@
  * ANativeWindow.
  */
 enum {
+// clang-format off
     NATIVE_WINDOW_SET_USAGE                 =  0,
     NATIVE_WINDOW_CONNECT                   =  1,   /* deprecated */
     NATIVE_WINDOW_DISCONNECT                =  2,   /* deprecated */
@@ -353,8 +354,14 @@
     NATIVE_WINDOW_SET_SURFACE_DAMAGE        = 20,   /* private */
     NATIVE_WINDOW_SET_SHARED_BUFFER_MODE    = 21,
     NATIVE_WINDOW_SET_AUTO_REFRESH          = 22,
-    NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS   = 23,
-    NATIVE_WINDOW_GET_FRAME_TIMESTAMPS      = 24,
+    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 */
@@ -1017,6 +1024,20 @@
     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)
 {
@@ -1024,19 +1045,40 @@
             enable);
 }
 
-static inline int native_window_get_frame_timestamps(
-        struct ANativeWindow* window, uint32_t framesAgo,
-        int64_t* outRequestedPresentTime, int64_t* outAcquireTime,
-        int64_t* outRefreshStartTime, int64_t* outGlCompositionDoneTime,
-        int64_t* outDisplayPresentTime, int64_t* outDisplayRetireTime,
-        int64_t* outReleaseTime)
+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_FRAME_TIMESTAMPS,
-            framesAgo, outRequestedPresentTime, outAcquireTime,
-            outRefreshStartTime, outGlCompositionDoneTime,
-            outDisplayPresentTime, outDisplayRetireTime, outReleaseTime);
+    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* outDisplayRetireTime,
+        int64_t* outDequeueReadyTime, int64_t* outReleaseTime)
+{
+    return window->perform(window, NATIVE_WINDOW_GET_FRAME_TIMESTAMPS,
+            frameId, outRequestedPresentTime, outAcquireTime, outLatchTime,
+            outFirstRefreshStartTime, outLastRefreshStartTime,
+            outGpuCompositionDoneTime, outDisplayPresentTime,
+            outDisplayRetireTime, 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/include/sysutils b/include/sysutils
new file mode 120000
index 0000000..1c8e85b
--- /dev/null
+++ b/include/sysutils
@@ -0,0 +1 @@
+../libsysutils/include/sysutils/
\ No newline at end of file
diff --git a/include/utils b/include/utils
new file mode 120000
index 0000000..e8476fd
--- /dev/null
+++ b/include/utils
@@ -0,0 +1 @@
+../libutils/include/utils/
\ No newline at end of file
diff --git a/include/ziparchive/zip_archive.h b/include/ziparchive/zip_archive.h
index 54946fc..31fc2df 100644
--- a/include/ziparchive/zip_archive.h
+++ b/include/ziparchive/zip_archive.h
@@ -26,8 +26,6 @@
 #include <sys/types.h>
 #include <utils/Compat.h>
 
-__BEGIN_DECLS
-
 /* Zip compression methods we support */
 enum {
   kCompressStored     = 0,        // no compression
@@ -228,6 +226,4 @@
         ProcessZipEntryFunction func, void* cookie);
 #endif
 
-__END_DECLS
-
 #endif  // LIBZIPARCHIVE_ZIPARCHIVE_H_
diff --git a/init/.clang-format b/init/.clang-format
new file mode 100644
index 0000000..48d423f
--- /dev/null
+++ b/init/.clang-format
@@ -0,0 +1,14 @@
+---
+Language:        Cpp
+BasedOnStyle:  Google
+BinPackArguments: true
+BinPackParameters: true
+ColumnLimit:     100
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+IndentWidth:     4
+Standard:        Auto
+TabWidth:        8
+UseTab:          Never
+DerivePointerAlignment: false
+PointerAlignment: Left
+...
diff --git a/init/Android.mk b/init/Android.mk
index ecdf5db..2fc6f19 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -54,7 +54,7 @@
     service.cpp \
     util.cpp \
 
-LOCAL_STATIC_LIBRARIES := libbase libselinux liblog libprocessgroup
+LOCAL_STATIC_LIBRARIES := libbase libselinux liblog libprocessgroup libnl
 LOCAL_WHOLE_STATIC_LIBRARIES := libcap
 LOCAL_MODULE := libinit
 LOCAL_SANITIZE := integer
@@ -92,7 +92,7 @@
     libsquashfs_utils \
     liblogwrap \
     libcutils \
-    libext4_utils_static \
+    libext4_utils \
     libbase \
     libc \
     libselinux \
@@ -101,9 +101,29 @@
     libcrypto \
     libc++_static \
     libdl \
-    libsparse_static \
+    libsparse \
     libz \
-    libprocessgroup
+    libprocessgroup \
+    libnl \
+    libavb
+
+# Include SELinux policy. We do this here because different modules
+# need to be included based on the value of PRODUCT_FULL_TREBLE. This
+# type of conditional inclusion cannot be done in top-level files such
+# as build/target/product/embedded.mk.
+# This conditional inclusion closely mimics the conditional logic
+# inside init/init.cpp for loading SELinux policy from files.
+ifeq ($(PRODUCT_FULL_TREBLE),true)
+# Use split SELinux policy
+LOCAL_REQUIRED_MODULES += \
+    mapping_sepolicy.cil \
+    nonplat_sepolicy.cil \
+    plat_sepolicy.cil \
+    secilc
+else
+# Use monolithic SELinux policy
+LOCAL_REQUIRED_MODULES += sepolicy
+endif
 
 # Create symlinks.
 LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
@@ -121,6 +141,7 @@
 LOCAL_MODULE := init_tests
 LOCAL_SRC_FILES := \
     init_parser_test.cpp \
+    property_service_test.cpp \
     util_test.cpp \
 
 LOCAL_SHARED_LIBRARIES += \
@@ -132,3 +153,8 @@
 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
new file mode 100644
index 0000000..d3dd73a
--- /dev/null
+++ b/init/README.md
@@ -0,0 +1,566 @@
+Android Init Language
+---------------------
+
+The Android Init Language consists of five broad classes of statements:
+Actions, Commands, Services, Options, and Imports.
+
+All of these are line-oriented, consisting of tokens separated by
+whitespace.  The c-style backslash escapes may be used to insert
+whitespace into a token.  Double quotes may also be used to prevent
+whitespace from breaking text into multiple tokens.  The backslash,
+when it is the last character on a line, may be used for line-folding.
+
+Lines which start with a # (leading whitespace allowed) are comments.
+
+Actions and Services implicitly declare a new section.  All commands
+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
+with the same name as an existing one, it is ignored and an error
+message is logged.
+
+
+Init .rc Files
+--------------
+The init language is used in plain text files that take the .rc file
+extension.  There are typically multiple of these in multiple
+locations on the system, described below.
+
+/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.
+
+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.
+This is primarily for supporting factory mode and other non-standard boot
+modes.  The three default paths should be used for the normal boot process.
+
+The intention of these directories is:
+
+   1. /system/etc/init/ is for core system items such as
+      SurfaceFlinger, MediaService, and logcatd.
+   2. /vendor/etc/init/ is for SoC vendor items such as actions or
+      daemons needed for core SoC functionality.
+   3. /odm/etc/init/ is for device manufacturer items such as
+      actions or daemons needed for motion sensor or other peripheral
+      functionality.
+
+All services whose binaries reside on the system, vendor, or odm
+partitions should have their service entries placed into a
+corresponding init .rc file, located in the /etc/init/
+directory of the partition where they reside.  There is a build
+system macro, LOCAL\_INIT\_RC, that handles this for developers.  Each
+init .rc file should additionally contain any actions associated with
+its service.
+
+An example is the logcatd.rc and Android.mk files located in the
+system/core/logcat directory.  The LOCAL\_INIT\_RC macro in the
+Android.mk file places logcatd.rc in /system/etc/init/ during the
+build process.  Init loads logcatd.rc during the mount\_all command and
+allows the service to be run and the action to be queued when
+appropriate.
+
+This break up of init .rc files according to their daemon is preferred
+to the previously used monolithic init .rc files.  This approach
+ensures that the only service entries that init reads and the only
+actions that init performs correspond to services whose binaries are in
+fact present on the file system, which was not the case with the
+monolithic init .rc files.  This additionally will aid in merge
+conflict resolution when multiple services are added to the system, as
+each one will go into a separate file.
+
+There are two options "early" and "late" in mount\_all command
+which can be set after optional paths. With "--early" set, the
+init executable will skip mounting entries with "latemount" flag
+and triggering fs encryption state event. With "--late" set,
+init executable will only mount entries with "latemount" flag but skip
+importing rc files. By default, no option is set, and mount\_all will
+process all entries in the given fstab.
+
+Actions
+-------
+Actions are named sequences of commands.  Actions have a trigger which
+is used to determine when the action should occur.  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).
+
+Each action in the queue is dequeued in sequence and each command in
+that action is executed in sequence.  Init handles other activities
+(device creation/destruction, property setting, process restarting)
+"between" the execution of the commands in activities.
+
+Actions take the form of:
+
+    on <trigger> [&& <trigger>]*
+       <command>
+       <command>
+       <command>
+
+
+Services
+--------
+Services are programs which init launches and (optionally) restarts
+when they exit.  Services take the form of:
+
+    service <name> <pathname> [ <argument> ]*
+       <option>
+       <option>
+       ...
+
+
+Options
+-------
+Options are modifiers to services.  They affect how and when init
+runs the service.
+
+`console [<console>]`
+> This service needs a console. The optional second parameter chooses a
+  specific console instead of the default. The default "/dev/console" can
+  be changed by setting the "androidboot.console" kernel parameter. In
+  all cases the leading "/dev/" should be omitted, so "/dev/tty0" would be
+  specified as just "console tty0".
+
+`critical`
+> This is a device-critical service. If it exits more than four times in
+  four minutes, the device will reboot into recovery mode.
+
+`disabled`
+> This service will not automatically start with its class.
+  It must be explicitly started by name.
+
+`setenv <name> <value>`
+> Set the environment variable _name_ to _value_ in the launched process.
+
+`socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`
+> Create a unix domain socket named /dev/socket/_name_ and pass its fd to the
+  launched process.  _type_ must be "dgram", "stream" or "seqpacket".  User and
+  group default to 0.  'seclabel' is the SELinux security context for the
+  socket.  It defaults to the service security context, as specified by
+  seclabel or computed based on the service executable file security context.
+  For native executables see libcutils android\_get\_control\_socket().
+
+`file <path> <type>`
+> Open a file path and pass its fd to the launched process. _type_ must be
+  "r", "w" or "rw".  For native executables see libcutils
+  android\_get\_control\_file().
+
+`user <username>`
+> Change to 'username' before exec'ing this service.
+  Currently defaults to root.  (??? probably should default to nobody)
+  As of Android M, processes should use this option even if they
+  require Linux capabilities.  Previously, to acquire Linux
+  capabilities, a process would need to run as root, request the
+  capabilities, then drop to its desired uid.  There is a new
+  mechanism through fs\_config that allows device manufacturers to add
+  Linux capabilities to specific binaries on a file system that should
+  be used instead. This mechanism is described on
+  <http://source.android.com/devices/tech/config/filesystem.html>.  When
+  using this new mechanism, processes can use the user option to
+  select their desired uid without ever running as root.
+  As of Android O, processes can also request capabilities directly in their .rc
+  files. See the "capabilities" option below.
+
+`group <groupname> [ <groupname>\* ]`
+> Change to 'groupname' before exec'ing this service.  Additional
+  groupnames beyond the (required) first one are used to set the
+  supplemental groups of the process (via setgroups()).
+  Currently defaults to root.  (??? probably should default to nobody)
+
+`capabilities <capability> [ <capability>\* ]`
+> Set capabilities when exec'ing this service. 'capability' should be a Linux
+  capability without the "CAP\_" prefix, like "NET\_ADMIN" or "SETPCAP". See
+  http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux
+  capabilities.
+
+`seclabel <seclabel>`
+> Change to 'seclabel' before exec'ing this service.
+  Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
+  Services on the system partition can instead use policy-defined transitions
+  based on their file security context.
+  If not specified and no transition is defined in policy, defaults to the init context.
+
+`oneshot`
+> Do not restart the service when it exits.
+
+`class <name>`
+> Specify a class name for the service.  All services in a
+  named class may be started or stopped together.  A service
+  is in the class "default" if one is not specified via the
+  class option.
+
+`onrestart`
+> Execute a Command (see below) when service restarts.
+
+`writepid <file...>`
+> Write the child's pid to the given files when it forks. Meant for
+  cgroup/cpuset usage.
+
+`priority <priority>`
+> Scheduling priority of the service process. This value has to be in range
+  -20 to 19. Default priority is 0. Priority is set via setpriority().
+
+`namespace <pid|mnt>`
+> Enter a new PID or mount namespace when forking the service.
+
+`oom_score_adjust <value>`
+> Sets the child's /proc/self/oom\_score\_adj to the specified value,
+  which must range from -1000 to 1000.
+
+
+Triggers
+--------
+Triggers are strings which can be used to match certain kinds of
+events and used to cause an action to occur.
+
+Triggers are subdivided into event triggers and property triggers.
+
+Event triggers are strings triggered by the 'trigger' command or by
+the QueueEventTrigger() function within the init executable.  These
+take the form of a simple string such as 'boot' or 'late-init'.
+
+Property triggers are strings triggered when a named property changes
+value to a given new value or when a named property changes value to
+any new value.  These take the form of 'property:<name>=<value>' and
+'property:<name>=\*' respectively.  Property triggers are additionally
+evaluated and triggered accordingly during the initial boot phase of
+init.
+
+An Action can have multiple property triggers but may only have one
+event trigger.
+
+For example:
+`on boot && property:a=b` defines an action that is only executed when
+the 'boot' event trigger happens and the property a equals b.
+
+`on property:a=b && property:c=d` defines an action that is executed
+at three times:
+
+   1. During initial boot if property a=b and property c=d.
+   2. Any time that property a transitions to value b, while property c already equals d.
+   3. Any time that property c transitions to value d, while property a already equals b.
+
+
+Commands
+--------
+
+`bootchart [start|stop]`
+> Start/stop bootcharting. These are present in the default init.rc files,
+  but bootcharting is only active if the file /data/bootchart/enabled exists;
+  otherwise bootchart start/stop are no-ops.
+
+`chmod <octal-mode> <path>`
+> Change file access permissions.
+
+`chown <owner> <group> <path>`
+> Change file owner and group.
+
+`class_start <serviceclass>`
+> Start all services of the specified class if they are
+  not already running.
+
+`class_stop <serviceclass>`
+> Stop and disable all services of the specified class if they are
+  currently running.
+
+`class_reset <serviceclass>`
+> Stop all services of the specified class if they are
+  currently running, without disabling them. They can be restarted
+  later using `class_start`.
+
+`copy <src> <dst>`
+> Copies a file. Similar to write, but useful for binary/large
+  amounts of data.
+
+`domainname <name>`
+> Set the domain name.
+
+`enable <servicename>`
+> Turns a disabled service into an enabled one as if the service did not
+  specify disabled.
+  If the service is supposed to be running, it will be started now.
+  Typically used when the bootloader sets a variable that indicates a specific
+  service should be started when needed. E.g.
+
+    on property:ro.boot.myfancyhardware=1
+        enable my_fancy_service_for_my_fancy_hardware
+
+`exec [ <seclabel> [ <user> [ <group>\* ] ] ] -- <command> [ <argument>\* ]`
+> Fork and execute command with the given arguments. The command starts
+  after "--" so that an optional security context, user, and supplementary
+  groups can be provided. No other commands will be run until this one
+  finishes. _seclabel_ can be a - to denote default. Properties are expanded
+  within _argument_.
+
+`export <name> <value>`
+> Set the environment variable _name_ equal to _value_ in the
+  global environment (which will be inherited by all processes
+  started after this command is executed)
+
+`hostname <name>`
+> Set the host name.
+
+`ifup <interface>`
+> Bring the network interface _interface_ online.
+
+`insmod [-f] <path> [<options>]`
+> Install the module at _path_ with the specified options.
+  -f: force installation of the module even if the version of the running kernel
+  and the version of the kernel for which the module was compiled do not match.
+
+`load_all_props`
+> Loads properties from /system, /vendor, et cetera.
+  This is included in the default init.rc.
+
+`load_persist_props`
+> Loads persistent properties when /data has been decrypted.
+  This is included in the default init.rc.
+
+`loglevel <level>`
+> Sets the kernel log level to level. Properties are expanded within _level_.
+
+`mkdir <path> [mode] [owner] [group]`
+> Create a directory at _path_, optionally with the given mode, owner, and
+  group. If not provided, the directory is created with permissions 755 and
+  owned by the root user and root group. If provided, the mode, owner and group
+  will be updated if the directory exists already.
+
+`mount_all <fstab> [ <path> ]\* [--<option>]`
+> Calls fs\_mgr\_mount\_all on the given fs\_mgr-format fstab and imports .rc files
+  at the specified paths (e.g., on the partitions just mounted) with optional
+  options "early" and "late".
+  Refer to the section of "Init .rc Files" for detail.
+
+`mount <type> <device> <dir> [ <flag>\* ] [<options>]`
+> Attempt to mount the named device at the directory _dir_
+  _flag_s include "ro", "rw", "remount", "noatime", ...
+  _options_ include "barrier=1", "noauto\_da\_alloc", "discard", ... as
+  a comma separated string, eg: barrier=1,noauto\_da\_alloc
+
+`powerctl`
+> Internal implementation detail used to respond to changes to the
+  "sys.powerctl" system property, used to implement rebooting.
+
+`restart <service>`
+> Like stop, but doesn't disable the service.
+
+`restorecon <path> [ <path>\* ]`
+> Restore the file named by _path_ to the security context specified
+  in the file\_contexts configuration.
+  Not required for directories created by the init.rc as these are
+  automatically labeled correctly by init.
+
+`restorecon_recursive <path> [ <path>\* ]`
+> Recursively restore the directory tree named by _path_ to the
+  security contexts specified in the file\_contexts configuration.
+
+`rm <path>`
+> Calls unlink(2) on the given path. You might want to
+  use "exec -- rm ..." instead (provided the system partition is
+  already mounted).
+
+`rmdir <path>`
+> Calls rmdir(2) on the given path.
+
+`setprop <name> <value>`
+> Set system property _name_ to _value_. Properties are expanded
+  within _value_.
+
+`setrlimit <resource> <cur> <max>`
+> Set the rlimit for a resource.
+
+`start <service>`
+> Start a service running if it is not already running.
+
+`stop <service>`
+> Stop a service from running if it is currently running.
+
+`swapon_all <fstab>`
+> Calls fs\_mgr\_swapon\_all on the given fstab file.
+
+`symlink <target> <path>`
+> Create a symbolic link at _path_ with the value _target_
+
+`sysclktz <mins_west_of_gmt>`
+> Set the system clock base (0 if system clock ticks in GMT)
+
+`trigger <event>`
+> Trigger an event.  Used to queue an action from another
+  action.
+
+`umount <path>`
+> Unmount the filesystem mounted at that path.
+
+`verity_load_state`
+> Internal implementation detail used to load dm-verity state.
+
+`verity_update_state <mount-point>`
+> Internal implementation detail used to update dm-verity state and
+  set the partition._mount-point_.verified properties used by adb remount
+  because fs\_mgr can't set them directly itself.
+
+`wait <path> [ <timeout> ]`
+> Poll for the existence of the given file and return when found,
+  or the timeout has been reached. If timeout is not specified it
+  currently defaults to five seconds.
+
+`wait_for_prop <name> <value>`
+> Wait for system property _name_ to be _value_. Properties are expanded
+  within _value_. If property _name_ is already set to _value_, continue
+  immediately.
+
+`write <path> <content>`
+> Open the file at _path_ and write a string to it with write(2).
+  If the file does not exist, it will be created. If it does exist,
+  it will be truncated. Properties are expanded within _content_.
+
+
+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:
+
+   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
+
+
+Properties
+----------
+Init provides information about the services that it is responsible
+for via the below properties.
+
+`init.svc.<name>`
+> State of a named service ("stopped", "stopping", "running", "restarting")
+
+
+Boot timing
+-----------
+Init records some boot timing information in system properties.
+
+`ro.boottime.init`
+> Time after boot in ns (via the CLOCK\_BOOTTIME clock) at which the first
+  stage of init started.
+
+`ro.boottime.init.selinux`
+> How long it took the first stage to initialize SELinux.
+
+`ro.boottime.init.cold_boot_wait`
+> How long init waited for ueventd's coldboot phase to end.
+
+`ro.boottime.<service-name>`
+> Time after boot in ns (via the CLOCK\_BOOTTIME clock) that the service was
+  first started.
+
+
+Bootcharting
+------------
+This version of init contains code to perform "bootcharting": generating log
+files that can be later processed by the tools provided by <http://www.bootchart.org/>.
+
+On the emulator, use the -bootchart _timeout_ option to boot with bootcharting
+activated for _timeout_ seconds.
+
+On a device:
+
+    adb shell 'touch /data/bootchart/enabled'
+
+Don't forget to delete this file when you're done collecting data!
+
+The log files are written to /data/bootchart/. A script is provided to
+retrieve them and create a bootchart.tgz file that can be used with the
+bootchart command-line utility:
+
+    sudo apt-get install pybootchartgui
+    # grab-bootchart.sh uses $ANDROID_SERIAL.
+    $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
+
+One thing to watch for is that the bootchart will show init as if it started
+running at 0s. You'll have to look at dmesg to work out when the kernel
+actually started init.
+
+
+Comparing two bootcharts
+------------------------
+A handy script named compare-bootcharts.py can be used to compare the
+start/end time of selected processes. The aforementioned grab-bootchart.sh
+will leave a bootchart tarball named bootchart.tgz at /tmp/android-bootchart.
+If two such barballs are preserved on the host machine under different
+directories, the script can list the timestamps differences. For example:
+
+Usage: system/core/init/compare-bootcharts.py _base-bootchart-dir_ _exp-bootchart-dir_
+
+    process: baseline experiment (delta) - Unit is ms (a jiffy is 10 ms on the system)
+    ------------------------------------
+    /init: 50 40 (-10)
+    /system/bin/surfaceflinger: 4320 4470 (+150)
+    /system/bin/bootanimation: 6980 6990 (+10)
+    zygote64: 10410 10640 (+230)
+    zygote: 10410 10640 (+230)
+    system_server: 15350 15150 (-200)
+    bootanimation ends at: 33790 31230 (-2560)
+
+
+Systrace
+--------
+Systrace (<http://developer.android.com/tools/help/systrace.html>) can be
+used for obtaining performance analysis reports during boot
+time on userdebug or eng builds.
+
+Here is an example of trace events of "wm" and "am" categories:
+
+    $ANDROID_BUILD_TOP/external/chromium-trace/systrace.py \
+          wm am --boot
+
+This command will cause the device to reboot. After the device is rebooted and
+the boot sequence has finished, the trace report is obtained from the device
+and written as trace.html on the host by hitting Ctrl+C.
+
+Limitation: recording trace events is started after persistent properties are loaded, so
+the trace events that are emitted before that are not recorded. Several
+services such as vold, surfaceflinger, and servicemanager are affected by this
+limitation since they are started before persistent properties are loaded.
+Zygote initialization and the processes that are forked from the zygote are not
+affected.
+
+
+Debugging init
+--------------
+By default, programs executed by init will drop stdout and stderr into
+/dev/null. To help with debugging, you can execute your program via the
+Android program logwrapper. This will redirect stdout/stderr into the
+Android logging system (accessed via logcat).
+
+For example
+service akmd /system/bin/logwrapper /sbin/akmd
+
+For quicker turnaround when working on init itself, use:
+
+    mm -j &&
+    m ramdisk-nodeps &&
+    m bootimage-nodeps &&
+    adb reboot bootloader &&
+    fastboot boot $ANDROID_PRODUCT_OUT/boot.img
+
+Alternatively, use the emulator:
+
+    emulator -partition-size 1024 \
+        -verbose -show-kernel -no-window
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 8fb55f0..4a9c32e 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -15,7 +15,7 @@
  */
 
 #include "bootchart.h"
-#include "log.h"
+
 #include "property_service.h"
 
 #include <dirent.h>
@@ -29,247 +29,170 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <chrono>
+#include <condition_variable>
 #include <memory>
+#include <mutex>
 #include <string>
+#include <thread>
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 
-#define LOG_ROOT        "/data/bootchart"
-#define LOG_STAT        LOG_ROOT"/proc_stat.log"
-#define LOG_PROCS       LOG_ROOT"/proc_ps.log"
-#define LOG_DISK        LOG_ROOT"/proc_diskstats.log"
-#define LOG_HEADER      LOG_ROOT"/header"
-#define LOG_ACCT        LOG_ROOT"/kernel_pacct"
+using android::base::StringPrintf;
+using namespace std::chrono_literals;
 
-#define LOG_STARTFILE   LOG_ROOT"/start"
-#define LOG_STOPFILE    LOG_ROOT"/stop"
+static std::thread* g_bootcharting_thread;
 
-// Polling period in ms.
-static const int BOOTCHART_POLLING_MS = 200;
-
-// Max polling time in seconds.
-static const int BOOTCHART_MAX_TIME_SEC = 10*60;
-
-static long long g_last_bootchart_time;
-static int g_remaining_samples;
-
-static FILE* log_stat;
-static FILE* log_procs;
-static FILE* log_disks;
+static std::mutex g_bootcharting_finished_mutex;
+static std::condition_variable g_bootcharting_finished_cv;
+static bool g_bootcharting_finished;
 
 static long long get_uptime_jiffies() {
-    std::string uptime;
-    if (!android::base::ReadFileToString("/proc/uptime", &uptime)) {
-        return 0;
-    }
-    return 100LL * strtod(uptime.c_str(), NULL);
+  std::string uptime;
+  if (!android::base::ReadFileToString("/proc/uptime", &uptime)) return 0;
+  return 100LL * strtod(uptime.c_str(), NULL);
+}
+
+static std::unique_ptr<FILE, decltype(&fclose)> fopen_unique(const char* filename,
+                                                             const char* mode) {
+  std::unique_ptr<FILE, decltype(&fclose)> result(fopen(filename, mode), fclose);
+  if (!result) PLOG(ERROR) << "bootchart: failed to open " << filename;
+  return result;
 }
 
 static void log_header() {
-    char date[32];
-    time_t now_t = time(NULL);
-    struct tm now = *localtime(&now_t);
-    strftime(date, sizeof(date), "%F %T", &now);
+  char date[32];
+  time_t now_t = time(NULL);
+  struct tm now = *localtime(&now_t);
+  strftime(date, sizeof(date), "%F %T", &now);
 
-    utsname uts;
-    if (uname(&uts) == -1) {
-        return;
-    }
+  utsname uts;
+  if (uname(&uts) == -1) return;
 
-    std::string fingerprint = property_get("ro.build.fingerprint");
-    if (fingerprint.empty()) {
-        return;
-    }
+  std::string fingerprint = property_get("ro.build.fingerprint");
+  if (fingerprint.empty()) return;
 
-    std::string kernel_cmdline;
-    android::base::ReadFileToString("/proc/cmdline", &kernel_cmdline);
+  std::string kernel_cmdline;
+  android::base::ReadFileToString("/proc/cmdline", &kernel_cmdline);
 
-    FILE* out = fopen(LOG_HEADER, "we");
-    if (out == NULL) {
-        return;
-    }
-    fprintf(out, "version = Android init 0.8\n");
-    fprintf(out, "title = Boot chart for Android (%s)\n", date);
-    fprintf(out, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine);
-    fprintf(out, "system.release = %s\n", fingerprint.c_str());
-    // TODO: use /proc/cpuinfo "model name" line for x86, "Processor" line for arm.
-    fprintf(out, "system.cpu = %s\n", uts.machine);
-    fprintf(out, "system.kernel.options = %s\n", kernel_cmdline.c_str());
-    fclose(out);
+  auto fp = fopen_unique("/data/bootchart/header", "we");
+  if (!fp) return;
+  fprintf(&*fp, "version = Android init 0.8\n");
+  fprintf(&*fp, "title = Boot chart for Android (%s)\n", date);
+  fprintf(&*fp, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine);
+  fprintf(&*fp, "system.release = %s\n", fingerprint.c_str());
+  // TODO: use /proc/cpuinfo "model name" line for x86, "Processor" line for arm.
+  fprintf(&*fp, "system.cpu = %s\n", uts.machine);
+  fprintf(&*fp, "system.kernel.options = %s\n", kernel_cmdline.c_str());
 }
 
-static void do_log_uptime(FILE* log) {
-    fprintf(log, "%lld\n", get_uptime_jiffies());
+static void log_uptime(FILE* log) {
+  fprintf(log, "%lld\n", get_uptime_jiffies());
 }
 
-static void do_log_file(FILE* log, const char* procfile) {
-    do_log_uptime(log);
+static void log_file(FILE* log, const char* procfile) {
+  log_uptime(log);
 
-    std::string content;
-    if (android::base::ReadFileToString(procfile, &content)) {
-        fprintf(log, "%s\n", content.c_str());
-    }
+  std::string content;
+  if (android::base::ReadFileToString(procfile, &content)) {
+    fprintf(log, "%s\n", content.c_str());
+  }
 }
 
-static void do_log_procs(FILE* log) {
-    do_log_uptime(log);
+static void log_processes(FILE* log) {
+  log_uptime(log);
 
-    std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir("/proc"), closedir);
-    struct dirent* entry;
-    while ((entry = readdir(dir.get())) != NULL) {
-        // Only match numeric values.
-        char* end;
-        int pid = strtol(entry->d_name, &end, 10);
-        if (end != NULL && end > entry->d_name && *end == 0) {
-            char filename[32];
+  std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir("/proc"), closedir);
+  struct dirent* entry;
+  while ((entry = readdir(dir.get())) != NULL) {
+    // Only match numeric values.
+    int pid = atoi(entry->d_name);
+    if (pid == 0) continue;
 
-            // /proc/<pid>/stat only has truncated task names, so get the full
-            // name from /proc/<pid>/cmdline.
-            snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
-            std::string cmdline;
-            android::base::ReadFileToString(filename, &cmdline);
-            const char* full_name = cmdline.c_str(); // So we stop at the first NUL.
+    // /proc/<pid>/stat only has truncated task names, so get the full
+    // name from /proc/<pid>/cmdline.
+    std::string cmdline;
+    android::base::ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &cmdline);
+    const char* full_name = cmdline.c_str(); // So we stop at the first NUL.
 
-            // Read process stat line.
-            snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
-            std::string stat;
-            if (android::base::ReadFileToString(filename, &stat)) {
-                if (!cmdline.empty()) {
-                    // Substitute the process name with its real name.
-                    size_t open = stat.find('(');
-                    size_t close = stat.find_last_of(')');
-                    if (open != std::string::npos && close != std::string::npos) {
-                        stat.replace(open + 1, close - open - 1, full_name);
-                    }
-                }
-                fputs(stat.c_str(), log);
-            }
+    // Read process stat line.
+    std::string stat;
+    if (android::base::ReadFileToString(StringPrintf("/proc/%d/stat", pid), &stat)) {
+      if (!cmdline.empty()) {
+        // Substitute the process name with its real name.
+        size_t open = stat.find('(');
+        size_t close = stat.find_last_of(')');
+        if (open != std::string::npos && close != std::string::npos) {
+          stat.replace(open + 1, close - open - 1, full_name);
         }
+      }
+      fputs(stat.c_str(), log);
     }
+  }
 
-    fputc('\n', log);
+  fputc('\n', log);
 }
 
-static int bootchart_init() {
-    int timeout = 0;
+static void bootchart_thread_main() {
+  LOG(INFO) << "Bootcharting started";
 
-    std::string start;
-    android::base::ReadFileToString(LOG_STARTFILE, &start);
-    if (!start.empty()) {
-        timeout = atoi(start.c_str());
-    } else {
-        // When running with emulator, androidboot.bootchart=<timeout>
-        // might be passed by as kernel parameters to specify the bootchart
-        // timeout. this is useful when using -wipe-data since the /data
-        // partition is fresh.
-        std::string cmdline;
-        const char* s;
-        android::base::ReadFileToString("/proc/cmdline", &cmdline);
-#define KERNEL_OPTION  "androidboot.bootchart="
-        if ((s = strstr(cmdline.c_str(), KERNEL_OPTION)) != NULL) {
-            timeout = atoi(s + sizeof(KERNEL_OPTION) - 1);
-        }
-    }
-    if (timeout == 0)
-        return 0;
+  // Open log files.
+  auto stat_log = fopen_unique("/data/bootchart/proc_stat.log", "we");
+  if (!stat_log) return;
+  auto proc_log = fopen_unique("/data/bootchart/proc_ps.log", "we");
+  if (!proc_log) return;
+  auto disk_log = fopen_unique("/data/bootchart/proc_diskstats.log", "we");
+  if (!disk_log) return;
 
-    if (timeout > BOOTCHART_MAX_TIME_SEC)
-        timeout = BOOTCHART_MAX_TIME_SEC;
+  log_header();
 
-    int count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
-
-    log_stat = fopen(LOG_STAT, "we");
-    if (log_stat == NULL) {
-        return -1;
-    }
-    log_procs = fopen(LOG_PROCS, "we");
-    if (log_procs == NULL) {
-        fclose(log_stat);
-        return -1;
-    }
-    log_disks = fopen(LOG_DISK, "we");
-    if (log_disks == NULL) {
-        fclose(log_stat);
-        fclose(log_procs);
-        return -1;
+  while (true) {
+    {
+      std::unique_lock<std::mutex> lock(g_bootcharting_finished_mutex);
+      g_bootcharting_finished_cv.wait_for(lock, 200ms);
+      if (g_bootcharting_finished) break;
     }
 
-    // Create kernel process accounting file.
-    close(open(LOG_ACCT, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
-    acct(LOG_ACCT);
+    log_file(&*stat_log, "/proc/stat");
+    log_file(&*disk_log, "/proc/diskstats");
+    log_processes(&*proc_log);
+  }
 
-    log_header();
-    return count;
+  LOG(INFO) << "Bootcharting finished";
 }
 
-int do_bootchart_init(const std::vector<std::string>& args) {
-    g_remaining_samples = bootchart_init();
-    if (g_remaining_samples < 0) {
-        PLOG(ERROR) << "Bootcharting initialization failed";
-    } else if (g_remaining_samples > 0) {
-        LOG(INFO) << "Bootcharting started (will run for "
-                  << ((g_remaining_samples * BOOTCHART_POLLING_MS) / 1000) << " s).";
-    } else {
-        LOG(VERBOSE) << "Not bootcharting.";
-    }
+static int do_bootchart_start() {
+  // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
+  std::string start;
+  if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
+    LOG(VERBOSE) << "Not bootcharting";
     return 0;
+  }
+
+  g_bootcharting_thread = new std::thread(bootchart_thread_main);
+  return 0;
 }
 
-static int bootchart_step() {
-    do_log_file(log_stat,   "/proc/stat");
-    do_log_file(log_disks,  "/proc/diskstats");
-    do_log_procs(log_procs);
+static int do_bootchart_stop() {
+  if (!g_bootcharting_thread) return 0;
 
-    // Stop if /data/bootchart/stop contains 1.
-    std::string stop;
-    if (android::base::ReadFileToString(LOG_STOPFILE, &stop) && stop == "1") {
-        return -1;
-    }
+  // Tell the worker thread it's time to quit.
+  {
+    std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex);
+    g_bootcharting_finished = true;
+    g_bootcharting_finished_cv.notify_one();
+  }
 
-    return 0;
+  g_bootcharting_thread->join();
+  delete g_bootcharting_thread;
+  g_bootcharting_thread = nullptr;
+  return 0;
 }
 
-/* called to get time (in ms) used by bootchart */
-static long long bootchart_gettime() {
-    return 10LL*get_uptime_jiffies();
-}
-
-static void bootchart_finish() {
-    unlink(LOG_STOPFILE);
-    fclose(log_stat);
-    fclose(log_disks);
-    fclose(log_procs);
-    acct(NULL);
-    LOG(INFO) << "Bootcharting finished";
-}
-
-void bootchart_sample(int* timeout) {
-    // Do we have any more bootcharting to do?
-    if (g_remaining_samples <= 0) {
-        return;
-    }
-
-    long long current_time = bootchart_gettime();
-    int elapsed_time = current_time - g_last_bootchart_time;
-
-    if (elapsed_time >= BOOTCHART_POLLING_MS) {
-        // Count missed samples.
-        while (elapsed_time >= BOOTCHART_POLLING_MS) {
-            elapsed_time -= BOOTCHART_POLLING_MS;
-            g_remaining_samples--;
-        }
-        // Count may be negative, take a sample anyway.
-        g_last_bootchart_time = current_time;
-        if (bootchart_step() < 0 || g_remaining_samples <= 0) {
-            bootchart_finish();
-            g_remaining_samples = 0;
-        }
-    }
-    if (g_remaining_samples > 0) {
-        int remaining_time = BOOTCHART_POLLING_MS - elapsed_time;
-        if (*timeout < 0 || *timeout > remaining_time) {
-            *timeout = remaining_time;
-        }
-    }
+int do_bootchart(const std::vector<std::string>& args) {
+  if (args[1] == "start") return do_bootchart_start();
+  return do_bootchart_stop();
 }
diff --git a/init/bootchart.h b/init/bootchart.h
index 47eda7a..0e3593d 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -20,7 +20,6 @@
 #include <string>
 #include <vector>
 
-int do_bootchart_init(const std::vector<std::string>& args);
-void bootchart_sample(int* timeout);
+int do_bootchart(const std::vector<std::string>& args);
 
 #endif /* _BOOTCHART_H */
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 42dd0c6..507c4a0 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -38,6 +38,7 @@
 #include <linux/loop.h>
 #include <linux/module.h>
 
+#include <string>
 #include <thread>
 
 #include <selinux/android.h>
@@ -67,6 +68,8 @@
 #include "signal_handler.h"
 #include "util.h"
 
+using namespace std::literals::string_literals;
+
 #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
 #define UNMOUNT_CHECK_TIMES 10
 
@@ -139,8 +142,7 @@
     }
 }
 
-static int wipe_data_via_recovery(const std::string& reason) {
-    const std::vector<std::string> options = {"--wipe_data", std::string() + "--reason=" + reason};
+static int reboot_into_recovery(const std::vector<std::string>& options) {
     std::string err;
     if (!write_bootloader_message(options, &err)) {
         LOG(ERROR) << "failed to set bootloader message: " << err;
@@ -247,7 +249,7 @@
 }
 
 static int do_domainname(const std::vector<std::string>& args) {
-    return write_file("/proc/sys/kernel/domainname", args[1].c_str());
+    return write_file("/proc/sys/kernel/domainname", args[1].c_str()) ? 0 : 1;
 }
 
 static int do_enable(const std::vector<std::string>& args) {
@@ -263,10 +265,14 @@
     if (!svc) {
         return -1;
     }
-    if (!svc->Start()) {
+    if (!start_waiting_for_exec()) {
         return -1;
     }
-    waiting_for_exec = true;
+    if (!svc->Start()) {
+        stop_waiting_for_exec();
+        ServiceManager::GetInstance().RemoveService(*svc);
+        return -1;
+    }
     return 0;
 }
 
@@ -275,7 +281,7 @@
 }
 
 static int do_hostname(const std::vector<std::string>& args) {
-    return write_file("/proc/sys/kernel/hostname", args[1].c_str());
+    return write_file("/proc/sys/kernel/hostname", args[1].c_str()) ? 0 : 1;
 }
 
 static int do_ifup(const std::vector<std::string>& args) {
@@ -338,7 +344,10 @@
 
     if (e4crypt_is_native()) {
         if (e4crypt_set_directory_policy(args[1].c_str())) {
-            wipe_data_via_recovery(std::string() + "set_policy_failed:" + args[1]);
+            const std::vector<std::string> options = {
+                "--prompt_and_wipe_data",
+                "--reason=set_policy_failed:"s + args[1]};
+            reboot_into_recovery(options);
             return -1;
         }
     }
@@ -467,15 +476,18 @@
 static void import_late(const std::vector<std::string>& args, size_t start_index, size_t end_index) {
     Parser& parser = Parser::GetInstance();
     if (end_index <= start_index) {
-        // Use the default set if no path is given
-        static const std::vector<std::string> init_directories = {
-            "/system/etc/init",
-            "/vendor/etc/init",
-            "/odm/etc/init"
-        };
-
-        for (const auto& dir : init_directories) {
-            parser.ParseConfig(dir);
+        // Fallbacks for partitions on which early mount isn't enabled.
+        if (!parser.is_system_etc_init_loaded()) {
+            parser.ParseConfig("/system/etc/init");
+            parser.set_is_system_etc_init_loaded(true);
+        }
+        if (!parser.is_vendor_etc_init_loaded()) {
+            parser.ParseConfig("/vendor/etc/init");
+            parser.set_is_vendor_etc_init_loaded(true);
+        }
+        if (!parser.is_odm_etc_init_loaded()) {
+            parser.ParseConfig("/odm/etc/init");
+            parser.set_is_odm_etc_init_loaded(true);
         }
     } else {
         for (size_t i = start_index; i < end_index; ++i) {
@@ -559,7 +571,8 @@
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
         /* Setup a wipe via recovery, and reboot into recovery */
         PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
-        ret = wipe_data_via_recovery("wipe_data_via_recovery");
+        const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
+        ret = reboot_into_recovery(options);
         /* If reboot worked, there is no return. */
     } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
         if (e4crypt_install_keyring()) {
@@ -591,20 +604,27 @@
     int mount_mode = MOUNT_MODE_DEFAULT;
     const char* fstabfile = args[1].c_str();
     std::size_t path_arg_end = args.size();
+    const char* prop_post_fix = "default";
 
     for (na = args.size() - 1; na > 1; --na) {
         if (args[na] == "--early") {
             path_arg_end = na;
             queue_event = false;
             mount_mode = MOUNT_MODE_EARLY;
+            prop_post_fix = "early";
         } else if (args[na] == "--late") {
             path_arg_end = na;
             import_rc = false;
             mount_mode = MOUNT_MODE_LATE;
+            prop_post_fix = "late";
         }
     }
 
+    std::string prop_name = android::base::StringPrintf("ro.boottime.init.mount_all.%s",
+                                                        prop_post_fix);
+    Timer t;
     int ret =  mount_fstab(fstabfile, mount_mode);
+    property_set(prop_name.c_str(), std::to_string(t.duration_ms()).c_str());
 
     if (import_rc) {
         /* Paths of .rc files are specified at the 2nd argument and beyond */
@@ -694,6 +714,9 @@
     } else if (strncmp(command, "reboot", 6) == 0) {
         cmd = ANDROID_RB_RESTART2;
         len = 6;
+    } else if (strncmp(command, "thermal-shutdown", 16) == 0) {
+        cmd = ANDROID_RB_THERMOFF;
+        len = 16;
     } else {
         LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
         return -EINVAL;
@@ -808,7 +831,7 @@
 static int do_write(const std::vector<std::string>& args) {
     const char* path = args[1].c_str();
     const char* value = args[2].c_str();
-    return write_file(path, value);
+    return write_file(path, value) ? 0 : 1;
 }
 
 static int do_copy(const std::vector<std::string>& args) {
@@ -997,6 +1020,29 @@
     return -1;
 }
 
+static int do_wait_for_prop(const std::vector<std::string>& args) {
+    const char* name = args[1].c_str();
+    const char* value = args[2].c_str();
+    size_t value_len = strlen(value);
+
+    if (!is_legal_property_name(name)) {
+        LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
+                   << "\") failed: bad name";
+        return -1;
+    }
+    if (value_len >= PROP_VALUE_MAX) {
+        LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
+                   << "\") failed: value too long";
+        return -1;
+    }
+    if (!start_waiting_for_property(name, value)) {
+        LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
+                   << "\") failed: init already in waiting";
+        return -1;
+    }
+    return 0;
+}
+
 /*
  * Callback to make a directory from the ext4 code
  */
@@ -1028,7 +1074,7 @@
 BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     static const Map builtin_functions = {
-        {"bootchart_init",          {0,     0,    do_bootchart_init}},
+        {"bootchart",               {1,     1,    do_bootchart}},
         {"chmod",                   {2,     2,    do_chmod}},
         {"chown",                   {2,     3,    do_chown}},
         {"class_reset",             {1,     1,    do_class_reset}},
@@ -1068,6 +1114,7 @@
         {"verity_load_state",       {0,     0,    do_verity_load_state}},
         {"verity_update_state",     {0,     0,    do_verity_update_state}},
         {"wait",                    {1,     2,    do_wait}},
+        {"wait_for_prop",           {2,     2,    do_wait_for_prop}},
         {"write",                   {2,     2,    do_write}},
     };
     return builtin_functions;
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index 4592adc..b8a9ec0 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -25,8 +25,7 @@
 
 #define CAP_MAP_ENTRY(cap) { #cap, CAP_##cap }
 
-namespace {
-const std::map<std::string, int> cap_map = {
+static const std::map<std::string, int> cap_map = {
     CAP_MAP_ENTRY(CHOWN),
     CAP_MAP_ENTRY(DAC_OVERRIDE),
     CAP_MAP_ENTRY(DAC_READ_SEARCH),
@@ -69,9 +68,30 @@
 
 static_assert(CAP_LAST_CAP == CAP_AUDIT_READ, "CAP_LAST_CAP is not CAP_AUDIT_READ");
 
-bool DropBoundingSet(const CapSet& to_keep) {
-    for (size_t cap = 0; cap < to_keep.size(); ++cap) {
-        if (to_keep.test(cap)) {
+static bool ComputeCapAmbientSupported() {
+    return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >= 0;
+}
+
+static unsigned int ComputeLastValidCap() {
+    // Android does not support kernels < 3.8. 'CAP_WAKE_ALARM' has been present since 3.0, see
+    // http://lxr.free-electrons.com/source/include/linux/capability.h?v=3.0#L360.
+    unsigned int last_valid_cap = CAP_WAKE_ALARM;
+    for (; prctl(PR_CAPBSET_READ, last_valid_cap, 0, 0, 0) >= 0; ++last_valid_cap);
+
+    // |last_valid_cap| will be the first failing value.
+    return last_valid_cap - 1;
+}
+
+static bool DropBoundingSet(const CapSet& to_keep) {
+    unsigned int last_valid_cap = GetLastValidCap();
+    // When dropping the bounding set, attempt to drop capabilities reported at
+    // run-time, not at compile-time.
+    // If the run-time kernel is older than the compile-time headers, this
+    // avoids dropping an invalid capability. If the run-time kernel is newer
+    // than the headers, this guarantees all capabilities (even those unknown at
+    // compile time) will be dropped.
+    for (size_t cap = 0; cap <= last_valid_cap; ++cap) {
+        if (cap < to_keep.size() && to_keep.test(cap)) {
             // No need to drop this capability.
             continue;
         }
@@ -83,14 +103,14 @@
     return true;
 }
 
-bool SetProcCaps(const CapSet& to_keep, bool add_setpcap) {
+static bool SetProcCaps(const CapSet& to_keep, bool add_setpcap) {
     cap_t caps = cap_init();
     auto deleter = [](cap_t* p) { cap_free(*p); };
     std::unique_ptr<cap_t, decltype(deleter)> ptr_caps(&caps, deleter);
 
     cap_clear(caps);
     cap_value_t value[1];
-    for (size_t cap = 0; cap <= to_keep.size(); ++cap) {
+    for (size_t cap = 0; cap < to_keep.size(); ++cap) {
         if (to_keep.test(cap)) {
             value[0] = cap;
             if (cap_set_flag(caps, CAP_INHERITABLE, arraysize(value), value, CAP_SET) != 0 ||
@@ -117,7 +137,7 @@
     return true;
 }
 
-bool SetAmbientCaps(const CapSet& to_raise) {
+static bool SetAmbientCaps(const CapSet& to_raise) {
     for (size_t cap = 0; cap < to_raise.size(); ++cap) {
         if (to_raise.test(cap)) {
             if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) != 0) {
@@ -129,8 +149,6 @@
     return true;
 }
 
-}  // namespace anonymous
-
 int LookupCap(const std::string& cap_name) {
     auto e = cap_map.find(cap_name);
     if (e != cap_map.end()) {
@@ -140,6 +158,16 @@
     }
 }
 
+bool CapAmbientSupported() {
+    static bool cap_ambient_supported = ComputeCapAmbientSupported();
+    return cap_ambient_supported;
+}
+
+unsigned int GetLastValidCap() {
+    static unsigned int last_valid_cap = ComputeLastValidCap();
+    return last_valid_cap;
+}
+
 bool SetCapsForExec(const CapSet& to_keep) {
     // Need to keep SETPCAP to drop bounding set below.
     bool add_setpcap = true;
diff --git a/init/capabilities.h b/init/capabilities.h
index 368178d..abd7fb2 100644
--- a/init/capabilities.h
+++ b/init/capabilities.h
@@ -12,6 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#ifndef _INIT_CAPABILITIES_H
+#define _INIT_CAPABILITIES_H
+
 #include <linux/capability.h>
 
 #include <bitset>
@@ -20,4 +23,8 @@
 using CapSet = std::bitset<CAP_LAST_CAP + 1>;
 
 int LookupCap(const std::string& cap_name);
+bool CapAmbientSupported();
+unsigned int GetLastValidCap();
 bool SetCapsForExec(const CapSet& to_keep);
+
+#endif  // _INIT_CAPABILITIES_H
diff --git a/init/devices.cpp b/init/devices.cpp
index 6af237c..bd11f5f 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -62,19 +62,7 @@
 
 extern struct selabel_handle *sehandle;
 
-static int device_fd = -1;
-
-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;
-};
+static android::base::unique_fd device_fd;
 
 struct perms_ {
     char *name;
@@ -249,11 +237,13 @@
 
     mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
 
-    if (selabel_lookup_best_match(sehandle, &secontext, path, links, mode)) {
-        PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
-        return;
+    if (sehandle) {
+        if (selabel_lookup_best_match(sehandle, &secontext, path, links, mode)) {
+            PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
+            return;
+        }
+        setfscreatecon(secontext);
     }
-    setfscreatecon(secontext);
 
     dev = makedev(major, minor);
     /* Temporarily change egid to avoid race condition setting the gid of the
@@ -261,10 +251,13 @@
      * some device nodes, so the uid has to be set with chown() and is still
      * racy. Fixing the gid race at least fixed the issue with system_server
      * opening dynamic input devices under the AID_INPUT gid. */
-    setegid(gid);
+    if (setegid(gid)) {
+        PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
+        goto out;
+    }
     /* If the node already exists update its SELinux label to handle cases when
      * it was created with the wrong context during coldboot procedure. */
-    if (mknod(path, mode, dev) && (errno == EEXIST)) {
+    if (mknod(path, mode, dev) && (errno == EEXIST) && secontext) {
 
         char* fcon = nullptr;
         int rc = lgetfilecon(path, &fcon);
@@ -283,10 +276,14 @@
 
 out:
     chown(path, uid, -1);
-    setegid(AID_ROOT);
+    if (setegid(AID_ROOT)) {
+        PLOG(FATAL) << "setegid(AID_ROOT) failed";
+    }
 
-    freecon(secontext);
-    setfscreatecon(NULL);
+    if (secontext) {
+        freecon(secontext);
+        setfscreatecon(NULL);
+    }
 }
 
 static void add_platform_device(const char *path)
@@ -349,6 +346,19 @@
     }
 }
 
+static void destroy_platform_devices() {
+    struct listnode* node;
+    struct listnode* n;
+    struct platform_node* bus;
+
+    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);
+    }
+}
+
 /* 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 */
@@ -515,7 +525,7 @@
         return NULL;
     memset(links, 0, sizeof(char *) * 4);
 
-    LOG(INFO) << "found " << type << " device " << device;
+    LOG(VERBOSE) << "found " << type << " device " << device;
 
     snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device);
 
@@ -875,9 +885,15 @@
     }
 }
 
+static bool inline should_stop_coldboot(coldboot_action_t act)
+{
+    return (act == COLDBOOT_STOP || act == COLDBOOT_FINISH);
+}
+
 #define UEVENT_MSG_LEN  2048
 
-static inline void handle_device_fd_with(void (handle_uevent)(struct uevent*))
+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;
@@ -890,14 +906,18 @@
 
         struct uevent uevent;
         parse_event(msg, &uevent);
-        handle_uevent(&uevent);
+        coldboot_action_t act = handle_uevent(&uevent);
+        if (should_stop_coldboot(act))
+            return act;
     }
+
+    return COLDBOOT_CONTINUE;
 }
 
-void handle_device_fd()
+coldboot_action_t handle_device_fd(coldboot_callback fn)
 {
-    handle_device_fd_with(
-        [](struct uevent *uevent) {
+    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();
@@ -907,9 +927,21 @@
                 }
             }
 
-            handle_device_event(uevent);
-            handle_firmware_event(uevent);
+            // 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
@@ -921,21 +953,24 @@
 ** socket's buffer.
 */
 
-static void do_coldboot(DIR *d)
+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) {
+    if (fd >= 0) {
         write(fd, "add\n", 4);
         close(fd);
-        handle_device_fd();
+        act = handle_device_fd(fn);
+        if (should_stop_coldboot(act))
+            return act;
     }
 
-    while((de = readdir(d))) {
+    while (!should_stop_coldboot(act) && (de = readdir(d))) {
         DIR *d2;
 
         if(de->d_type != DT_DIR || de->d_name[0] == '.')
@@ -949,89 +984,40 @@
         if(d2 == 0)
             close(fd);
         else {
-            do_coldboot(d2);
+            act = do_coldboot(d2, fn);
             closedir(d2);
         }
     }
+
+    // default is always to continue looking for uevents
+    return act;
 }
 
-static void coldboot(const char *path)
+static coldboot_action_t coldboot(const char *path, coldboot_callback fn)
 {
     std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path), closedir);
-    if(d) {
-        do_coldboot(d.get());
-    }
-}
-
-static void early_uevent_handler(struct uevent *uevent, const char *base, bool is_block)
-{
-    const char *name;
-    char devpath[DEVPATH_LEN];
-
-    if (is_block && strncmp(uevent->subsystem, "block", 5))
-        return;
-
-    name = parse_device_name(uevent, MAX_DEV_NAME);
-    if (!name) {
-        LOG(ERROR) << "Failed to parse dev name from uevent: " << uevent->action
-                   << " " << uevent->partition_name << " " << uevent->partition_num
-                   << " " << uevent->major << ":" << uevent->minor;
-        return;
+    if (d) {
+        return do_coldboot(d.get(), fn);
     }
 
-    snprintf(devpath, sizeof(devpath), "%s%s", base, name);
-    make_dir(base, 0755);
-
-    dev_t dev = makedev(uevent->major, uevent->minor);
-    mode_t mode = 0600 | (is_block ? S_IFBLK : S_IFCHR);
-    mknod(devpath, mode, dev);
+    return COLDBOOT_CONTINUE;
 }
 
-void early_create_dev(const std::string& syspath, early_device_type dev_type)
-{
-    android::base::unique_fd dfd(open(syspath.c_str(), O_RDONLY));
-    if (dfd < 0) {
-        LOG(ERROR) << "Failed to open " << syspath;
-        return;
+void device_init(const char* path, coldboot_callback fn) {
+    if (!sehandle) {
+        sehandle = selinux_android_file_context_handle();
     }
-
-    android::base::unique_fd fd(openat(dfd, "uevent", O_WRONLY));
-    if (fd < 0) {
-        LOG(ERROR) << "Failed to open " << syspath << "/uevent";
-        return;
-    }
-
-    fcntl(device_fd, F_SETFL, O_NONBLOCK);
-
-    write(fd, "add\n", 4);
-    handle_device_fd_with(dev_type == EARLY_BLOCK_DEV ?
-        [](struct uevent *uevent) {
-            early_uevent_handler(uevent, "/dev/block/", true);
-        } :
-        [](struct uevent *uevent) {
-            early_uevent_handler(uevent, "/dev/", false);
-        });
-}
-
-int early_device_socket_open() {
-    device_fd = uevent_open_socket(256*1024, true);
-    return device_fd < 0;
-}
-
-void early_device_socket_close() {
-    close(device_fd);
-}
-
-void device_init() {
-    sehandle = selinux_android_file_context_handle();
-    selinux_status_open(true);
-
-    /* is 256K enough? udev uses 16MB! */
-    device_fd = uevent_open_socket(256*1024, true);
+    // open uevent socket and selinux status only if it hasn't been
+    // done before
     if (device_fd == -1) {
-        return;
+        /* 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);
     }
-    fcntl(device_fd, F_SETFL, O_NONBLOCK);
 
     if (access(COLDBOOT_DONE, F_OK) == 0) {
         LOG(VERBOSE) << "Skipping coldboot, already done!";
@@ -1039,13 +1025,34 @@
     }
 
     Timer t;
-    coldboot("/sys/class");
-    coldboot("/sys/block");
-    coldboot("/sys/devices");
-    close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
+    coldboot_action_t act;
+    if (!path) {
+        act = coldboot("/sys/class", fn);
+        if (!should_stop_coldboot(act)) {
+            act = coldboot("/sys/block", fn);
+            if (!should_stop_coldboot(act)) {
+                act = coldboot("/sys/devices", fn);
+            }
+        }
+    } else {
+        act = coldboot(path, fn);
+    }
+
+    // If we have a callback, then do as it says. If no, then the default is
+    // to always create COLDBOOT_DONE file.
+    if (!fn || (act == COLDBOOT_FINISH)) {
+        close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
+    }
+
     LOG(INFO) << "Coldboot took " << t;
 }
 
+void device_close() {
+    destroy_platform_devices();
+    device_fd.reset();
+    selinux_status_close();
+}
+
 int get_device_fd() {
     return device_fd;
 }
diff --git a/init/devices.h b/init/devices.h
index 8e9ab7d..26a064b 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -17,16 +17,37 @@
 #ifndef _INIT_DEVICES_H
 #define _INIT_DEVICES_H
 
+#include <functional>
 #include <sys/stat.h>
 
-extern void handle_device_fd();
-extern void device_init(void);
+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
+};
 
-enum early_device_type { EARLY_BLOCK_DEV, EARLY_CHAR_DEV };
+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;
+};
 
-extern int early_device_socket_open();
-extern void early_device_socket_close();
-extern void early_create_dev(const std::string& syspath, early_device_type dev_type);
+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();
 
 extern int add_dev_perms(const char *name, const char *attr,
                          mode_t perm, unsigned int uid,
diff --git a/init/grab-bootchart.sh b/init/grab-bootchart.sh
index d6082aa..c4ff6df 100755
--- a/init/grab-bootchart.sh
+++ b/init/grab-bootchart.sh
@@ -11,7 +11,7 @@
 LOGROOT=/data/bootchart
 TARBALL=bootchart.tgz
 
-FILES="header proc_stat.log proc_ps.log proc_diskstats.log kernel_pacct"
+FILES="header proc_stat.log proc_ps.log proc_diskstats.log"
 
 for f in $FILES; do
     adb "${@}" pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null
diff --git a/init/init.cpp b/init/init.cpp
index 60ee4f7..81f228c 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -43,14 +43,18 @@
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
 #include <cutils/fs.h>
 #include <cutils/iosched_policy.h>
 #include <cutils/list.h>
 #include <cutils/sockets.h>
+#include <libavb/libavb.h>
 #include <private/android_filesystem_config.h>
 
 #include <fstream>
 #include <memory>
+#include <set>
+#include <vector>
 
 #include "action.h"
 #include "bootchart.h"
@@ -82,10 +86,14 @@
 
 const char *ENV[32];
 
-bool waiting_for_exec = false;
+static std::unique_ptr<Timer> waiting_for_exec(nullptr);
 
 static int epoll_fd = -1;
 
+static std::unique_ptr<Timer> waiting_for_prop(nullptr);
+static std::string wait_prop_name;
+static std::string wait_prop_value;
+
 void register_epoll_handler(int fd, void (*fn)()) {
     epoll_event ev;
     ev.events = EPOLLIN;
@@ -127,10 +135,52 @@
     return -1;
 }
 
+bool start_waiting_for_exec()
+{
+    if (waiting_for_exec) {
+        return false;
+    }
+    waiting_for_exec.reset(new Timer());
+    return true;
+}
+
+void stop_waiting_for_exec()
+{
+    if (waiting_for_exec) {
+        LOG(INFO) << "Wait for exec took " << *waiting_for_exec;
+        waiting_for_exec.reset();
+    }
+}
+
+bool start_waiting_for_property(const char *name, const char *value)
+{
+    if (waiting_for_prop) {
+        return false;
+    }
+    if (property_get(name) != value) {
+        // Current property value is not equal to expected value
+        wait_prop_name = name;
+        wait_prop_value = value;
+        waiting_for_prop.reset(new Timer());
+    } else {
+        LOG(INFO) << "start_waiting_for_property(\""
+                  << name << "\", \"" << value << "\"): already set";
+    }
+    return true;
+}
+
 void property_changed(const char *name, const char *value)
 {
     if (property_triggers_enabled)
         ActionManager::GetInstance().QueuePropertyTrigger(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();
+        }
+    }
 }
 
 static void restart_processes()
@@ -177,7 +227,7 @@
         panic();
     }
 
-    property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration_ns()).c_str());
+    property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration_ms()).c_str());
     return 0;
 }
 
@@ -262,26 +312,18 @@
     panic();
 }
 
-#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
-#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
-
-/* __attribute__((unused)) due to lack of mips support: see mips block
- * in set_mmap_rnd_bits_action */
-static bool __attribute__((unused)) set_mmap_rnd_bits_min(int start, int min, bool compat) {
-    std::string path;
-    if (compat) {
-        path = MMAP_RND_COMPAT_PATH;
-    } else {
-        path = MMAP_RND_PATH;
-    }
+static bool set_highest_available_option_value(std::string path, int min, int max)
+{
     std::ifstream inf(path, std::fstream::in);
     if (!inf) {
         LOG(ERROR) << "Cannot open for reading: " << path;
         return false;
     }
-    while (start >= min) {
+
+    int current = max;
+    while (current >= min) {
         // try to write out new value
-        std::string str_val = std::to_string(start);
+        std::string str_val = std::to_string(current);
         std::ofstream of(path, std::fstream::out);
         if (!of) {
             LOG(ERROR) << "Cannot open for writing: " << path;
@@ -297,16 +339,33 @@
         if (str_val.compare(str_rec) == 0) {
             break;
         }
-        start--;
+        current--;
     }
     inf.close();
-    if (start < min) {
-        LOG(ERROR) << "Unable to set minimum required entropy " << min << " in " << path;
+
+    if (current < min) {
+        LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
         return false;
     }
     return true;
 }
 
+#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
+#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
+
+/* __attribute__((unused)) due to lack of mips support: see mips block
+ * in set_mmap_rnd_bits_action */
+static bool __attribute__((unused)) set_mmap_rnd_bits_min(int start, int min, bool compat) {
+    std::string path;
+    if (compat) {
+        path = MMAP_RND_COMPAT_PATH;
+    } else {
+        path = MMAP_RND_PATH;
+    }
+
+    return set_highest_available_option_value(path, min, start);
+}
+
 /*
  * Set /proc/sys/vm/mmap_rnd_bits and potentially
  * /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
@@ -349,13 +408,9 @@
     // TODO: add mips support b/27788820
     ret = 0;
 #else
-    ERROR("Unknown architecture\n");
+    LOG(ERROR) << "Unknown architecture";
 #endif
 
-#ifdef __BRILLO__
-    // TODO: b/27794137
-    ret = 0;
-#endif
     if (ret == -1) {
         LOG(ERROR) << "Unable to set adequate mmap entropy value!";
         security_failure();
@@ -363,6 +418,25 @@
     return ret;
 }
 
+#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
+#define KPTR_RESTRICT_MINVALUE 2
+#define KPTR_RESTRICT_MAXVALUE 4
+
+/* Set kptr_restrict to the highest available level.
+ *
+ * Aborts if unable to set this to an acceptable value.
+ */
+static int set_kptr_restrict_action(const std::vector<std::string>& args)
+{
+    std::string path = KPTR_RESTRICT_PATH;
+
+    if (!set_highest_available_option_value(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
+        LOG(ERROR) << "Unable to set adequate kptr_restrict value!";
+        security_failure();
+    }
+    return 0;
+}
+
 static int keychord_init_action(const std::vector<std::string>& args)
 {
     keychord_init();
@@ -425,28 +499,52 @@
     }
 }
 
-static void process_kernel_dt() {
-    static const char android_dir[] = "/proc/device-tree/firmware/android";
+static constexpr char android_dt_dir[] = "/proc/device-tree/firmware/android";
 
-    std::string file_name = StringPrintf("%s/compatible", android_dir);
+static bool is_dt_compatible() {
+    std::string dt_value;
+    std::string file_name = StringPrintf("%s/compatible", android_dt_dir);
 
-    std::string dt_file;
-    android::base::ReadFileToString(file_name, &dt_file);
-    if (!dt_file.compare("android,firmware")) {
-        LOG(ERROR) << "firmware/android is not compatible with 'android,firmware'";
-        return;
+    if (android::base::ReadFileToString(file_name, &dt_value)) {
+        // trim the trailing '\0' out, otherwise the comparison
+        // will produce false-negatives.
+        dt_value.resize(dt_value.size() - 1);
+        if (dt_value == "android,firmware") {
+            return true;
+        }
     }
 
-    std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(android_dir), closedir);
+    return false;
+}
+
+static bool is_dt_fstab_compatible() {
+    std::string dt_value;
+    std::string file_name = StringPrintf("%s/%s/compatible", android_dt_dir, "fstab");
+
+    if (android::base::ReadFileToString(file_name, &dt_value)) {
+        dt_value.resize(dt_value.size() - 1);
+        if (dt_value == "android,fstab") {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static void process_kernel_dt() {
+    if (!is_dt_compatible()) return;
+
+    std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(android_dt_dir), closedir);
     if (!dir) return;
 
+    std::string dt_file;
     struct dirent *dp;
     while ((dp = readdir(dir.get())) != NULL) {
         if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
             continue;
         }
 
-        file_name = StringPrintf("%s/%s", android_dir, dp->d_name);
+        std::string file_name = StringPrintf("%s/%s", android_dt_dir, dp->d_name);
 
         android::base::ReadFileToString(file_name, &dt_file);
         std::replace(dt_file.begin(), dt_file.end(), ',', '.');
@@ -521,6 +619,180 @@
     return 0;
 }
 
+/*
+ * Forks, executes the provided program in the child, and waits for the completion in the parent.
+ * Child's stderr is captured and logged using LOG(ERROR).
+ *
+ * Returns true if the child exited with status code 0, returns false otherwise.
+ */
+static bool fork_execve_and_wait_for_completion(const char* filename, char* const argv[],
+                                                char* const envp[]) {
+    // Create a pipe used for redirecting child process's output.
+    // * pipe_fds[0] is the FD the parent will use for reading.
+    // * pipe_fds[1] is the FD the child will use for writing.
+    int pipe_fds[2];
+    if (pipe(pipe_fds) == -1) {
+        PLOG(ERROR) << "Failed to create pipe";
+        return false;
+    }
+
+    pid_t child_pid = fork();
+    if (child_pid == -1) {
+        PLOG(ERROR) << "Failed to fork for " << filename;
+        return false;
+    }
+
+    if (child_pid == 0) {
+        // fork succeeded -- this is executing in the child process
+
+        // Close the pipe FD not used by this process
+        TEMP_FAILURE_RETRY(close(pipe_fds[0]));
+
+        // Redirect stderr to the pipe FD provided by the parent
+        if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
+            PLOG(ERROR) << "Failed to redirect stderr of " << filename;
+            _exit(127);
+            return false;
+        }
+        TEMP_FAILURE_RETRY(close(pipe_fds[1]));
+
+        if (execve(filename, argv, envp) == -1) {
+            PLOG(ERROR) << "Failed to execve " << filename;
+            return false;
+        }
+        // Unreachable because execve will have succeeded and replaced this code
+        // with child process's code.
+        _exit(127);
+        return false;
+    } else {
+        // fork succeeded -- this is executing in the original/parent process
+
+        // Close the pipe FD not used by this process
+        TEMP_FAILURE_RETRY(close(pipe_fds[1]));
+
+        // Log the redirected output of the child process.
+        // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
+        // As a result, we're buffering all output and logging it in one go at the end of the
+        // invocation, instead of logging it as it comes in.
+        const int child_out_fd = pipe_fds[0];
+        std::string child_output;
+        if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
+            PLOG(ERROR) << "Failed to capture full output of " << filename;
+        }
+        TEMP_FAILURE_RETRY(close(child_out_fd));
+        if (!child_output.empty()) {
+            // Log captured output, line by line, because LOG expects to be invoked for each line
+            std::istringstream in(child_output);
+            std::string line;
+            while (std::getline(in, line)) {
+                LOG(ERROR) << filename << ": " << line;
+            }
+        }
+
+        // Wait for child to terminate
+        int status;
+        if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
+            PLOG(ERROR) << "Failed to wait for " << filename;
+            return false;
+        }
+
+        if (WIFEXITED(status)) {
+            int status_code = WEXITSTATUS(status);
+            if (status_code == 0) {
+                return true;
+            } else {
+                LOG(ERROR) << filename << " exited with status " << status_code;
+            }
+        } else if (WIFSIGNALED(status)) {
+            LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
+        } else if (WIFSTOPPED(status)) {
+            LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
+        } else {
+            LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
+        }
+
+        return false;
+    }
+}
+
+static constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
+
+static bool selinux_is_split_policy_device() { return access(plat_policy_cil_file, R_OK) != -1; }
+
+/*
+ * Loads SELinux policy split across platform/system and non-platform/vendor files.
+ *
+ * Returns true upon success, false otherwise (failure cause is logged).
+ */
+static bool selinux_load_split_policy() {
+    // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
+    // * platform -- policy needed due to logic contained in the system image,
+    // * non-platform -- policy needed due to logic contained in the vendor image,
+    // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
+    //   with newer versions of platform policy.
+    //
+    // secilc is invoked to compile the above three policy files into a single monolithic policy
+    // file. This file is then loaded into the kernel.
+
+    LOG(INFO) << "Compiling SELinux policy";
+
+    // We store the output of the compilation on /dev because this is the most convenient tmpfs
+    // storage mount available this early in the boot sequence.
+    char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
+    android::base::unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
+    if (compiled_sepolicy_fd < 0) {
+        PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
+        return false;
+    }
+
+    const char* compile_args[] = {"/system/bin/secilc", plat_policy_cil_file, "-M", "true", "-c",
+                                  "30",  // TODO: pass in SELinux policy version from build system
+                                  "/vendor/etc/selinux/mapping_sepolicy.cil",
+                                  "/vendor/etc/selinux/nonplat_sepolicy.cil", "-o",
+                                  compiled_sepolicy,
+                                  // We don't care about file_contexts output by the compiler
+                                  "-f", "/sys/fs/selinux/null",  // /dev/null is not yet available
+                                  nullptr};
+
+    if (!fork_execve_and_wait_for_completion(compile_args[0], (char**)compile_args, (char**)ENV)) {
+        unlink(compiled_sepolicy);
+        return false;
+    }
+    unlink(compiled_sepolicy);
+
+    LOG(INFO) << "Loading compiled SELinux policy";
+    if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
+        LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Loads SELinux policy from a monolithic file.
+ *
+ * Returns true upon success, false otherwise (failure cause is logged).
+ */
+static bool selinux_load_monolithic_policy() {
+    LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
+    if (selinux_android_load_policy() < 0) {
+        PLOG(ERROR) << "Failed to load monolithic SELinux policy";
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Loads SELinux policy into the kernel.
+ *
+ * Returns true upon success, false otherwise (failure cause is logged).
+ */
+static bool selinux_load_policy() {
+    return selinux_is_split_policy_device() ? selinux_load_split_policy()
+                                            : selinux_load_monolithic_policy();
+}
+
 static void selinux_initialize(bool in_kernel_domain) {
     Timer t;
 
@@ -531,10 +803,9 @@
     selinux_set_callback(SELINUX_CB_AUDIT, cb);
 
     if (in_kernel_domain) {
-        LOG(INFO) << "Loading SELinux policy...";
-        if (selinux_android_load_policy() < 0) {
-            PLOG(ERROR) << "failed to load policy";
-            security_failure();
+        LOG(INFO) << "Loading SELinux policy";
+        if (!selinux_load_policy()) {
+            panic();
         }
 
         bool kernel_enforcing = (security_getenforce() == 1);
@@ -546,12 +817,12 @@
             }
         }
 
-        if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
+        if (!write_file("/sys/fs/selinux/checkreqprot", "0")) {
             security_failure();
         }
 
         // init's first stage can't set properties, so pass the time to the second stage.
-        setenv("INIT_SELINUX_TOOK", std::to_string(t.duration_ns()).c_str(), 1);
+        setenv("INIT_SELINUX_TOOK", std::to_string(t.duration_ms()).c_str(), 1);
     } else {
         selinux_init_all_handles();
     }
@@ -573,102 +844,191 @@
     }
 }
 
-/* Returns a new path consisting of base_path and the file name in reference_path. */
-static std::string get_path(const std::string& base_path, const std::string& reference_path) {
-    std::string::size_type pos = reference_path.rfind('/');
-    if (pos == std::string::npos) {
-        return base_path + '/' + reference_path;
-    } else {
-        return base_path + reference_path.substr(pos);
-    }
-}
+static bool early_mount_one(struct fstab_rec* rec) {
+    if (rec && fs_mgr_is_verified(rec)) {
+        // setup verity and create the dm-XX block device
+        // needed to mount this partition
+        int ret = fs_mgr_setup_verity(rec, false);
+        if (ret == FS_MGR_SETUP_VERITY_FAIL) {
+            PLOG(ERROR) << "early_mount: Failed to setup verity for '" << rec->mount_point << "'";
+            return false;
+        }
 
-/* Imports the fstab info from cmdline. */
-static std::string import_cmdline_fstab() {
-    std::string prefix, fstab, fstab_full;
-
-    import_kernel_cmdline(false,
-        [&](const std::string& key, const std::string& value, bool in_qemu __attribute__((__unused__))) {
-            if (key == "android.early.prefix") {
-                prefix = value;
-            } else if (key == "android.early.fstab") {
-                fstab = value;
+        // The exact block device name is added as a mount source by
+        // fs_mgr_setup_verity() in ->blk_device as "/dev/block/dm-XX"
+        // We create that device by running coldboot on /sys/block/dm-XX
+        std::string dm_device(basename(rec->blk_device));
+        std::string syspath = StringPrintf("/sys/block/%s", dm_device.c_str());
+        device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
+            if (uevent->device_name && !strcmp(dm_device.c_str(), uevent->device_name)) {
+                LOG(VERBOSE) << "early_mount: creating dm-verity device : " << dm_device;
+                return COLDBOOT_STOP;
             }
+            return COLDBOOT_CONTINUE;
         });
-    if (!fstab.empty()) {
-        // Convert "mmcblk0p09+/odm+ext4+ro+verify" to "mmcblk0p09 /odm ext4 ro verify"
-        std::replace(fstab.begin(), fstab.end(), '+', ' ');
-        for (const auto& entry : android::base::Split(fstab, "\n")) {
-            fstab_full += prefix + entry + '\n';
-        }
     }
-    return fstab_full;
+
+    if (rec && fs_mgr_do_mount_one(rec)) {
+        PLOG(ERROR) << "early_mount: Failed to mount '" << rec->mount_point << "'";
+        return false;
+    }
+
+    return true;
 }
 
-/* Early mount vendor and ODM partitions. The fstab info is read from kernel cmdline. */
-static void early_mount() {
-    std::string fstab_string = import_cmdline_fstab();
-    if (fstab_string.empty()) {
-        LOG(INFO) << "Failed to load vendor fstab from kernel cmdline";
+// Creates devices with uevent->partition_name matching one in the in/out
+// partition_names. Note that the partition_names MUST have A/B suffix
+// when A/B is used. Found partitions will then be removed from the
+// partition_names for caller to check which devices are NOT created.
+static void early_device_init(std::set<std::string>* partition_names) {
+    if (partition_names->empty()) {
         return;
     }
-    FILE *fstab_file = fmemopen((void *)fstab_string.c_str(), fstab_string.length(), "r");
-    if (!fstab_file) {
-        PLOG(ERROR) << "Failed to open fstab string as FILE";
-        return;
-    }
-    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_file(fstab_file), fs_mgr_free_fstab);
-    fclose(fstab_file);
-    if (!fstab) {
-        LOG(ERROR) << "Failed to parse fstab string: " << fstab_string;
-        return;
-    }
-    LOG(INFO) << "Loaded vendor fstab from cmdline";
-
-    if (early_device_socket_open()) {
-        LOG(ERROR) << "Failed to open device uevent socket";
-        return;
-    }
-
-    /* Create /dev/device-mapper for dm-verity */
-    early_create_dev("/sys/devices/virtual/misc/device-mapper", EARLY_CHAR_DEV);
-
-    for (int i = 0; i < fstab->num_entries; ++i) {
-        struct fstab_rec *rec = &fstab->recs[i];
-        std::string mount_point = rec->mount_point;
-        std::string syspath = rec->blk_device;
-
-        if (mount_point != "/vendor" && mount_point != "/odm")
-            continue;
-
-        /* Create mount target under /dev/block/ from sysfs via uevent */
-        LOG(INFO) << "Mounting " << mount_point << " from " << syspath << "...";
-        char *devpath = strdup(get_path("/dev/block", syspath).c_str());
-        if (!devpath) {
-            PLOG(ERROR) << "Failed to strdup dev path in early mount " << syspath;
-            continue;
-        }
-        rec->blk_device = devpath;
-        early_create_dev(syspath, EARLY_BLOCK_DEV);
-
-        int rc = fs_mgr_early_setup_verity(rec);
-        if (rc == FS_MGR_EARLY_SETUP_VERITY_SUCCESS) {
-            /* Mount target is changed to /dev/block/dm-<n>; initiate its creation from sysfs counterpart */
-            early_create_dev(get_path("/sys/devices/virtual/block", rec->blk_device), EARLY_BLOCK_DEV);
-        } else if (rc == FS_MGR_EARLY_SETUP_VERITY_FAIL) {
-            LOG(ERROR) << "Failed to set up dm-verity on " << rec->blk_device;
-            continue;
-        } else { /* FS_MGR_EARLY_SETUP_VERITY_NO_VERITY */
-            LOG(INFO) << "dm-verity disabled on debuggable device; mount directly on " << rec->blk_device;
+    device_init(nullptr, [=](uevent* uevent) -> coldboot_action_t {
+        if (!strncmp(uevent->subsystem, "firmware", 8)) {
+            return COLDBOOT_CONTINUE;
         }
 
-        mkdir(mount_point.c_str(), 0755);
-        rc = mount(rec->blk_device, mount_point.c_str(), rec->fs_type, rec->flags, rec->fs_options);
-        if (rc) {
-            PLOG(ERROR) << "Failed to mount on " << rec->blk_device;
+        // we need platform devices to create symlinks
+        if (!strncmp(uevent->subsystem, "platform", 8)) {
+            return COLDBOOT_CREATE;
+        }
+
+        // Ignore everything that is not a block device
+        if (strncmp(uevent->subsystem, "block", 5)) {
+            return COLDBOOT_CONTINUE;
+        }
+
+        if (uevent->partition_name) {
+            // match partition names to create device nodes for partitions
+            // both partition_names and uevent->partition_name have A/B suffix when A/B is used
+            auto iter = partition_names->find(uevent->partition_name);
+            if (iter != partition_names->end()) {
+                LOG(VERBOSE) << "early_mount: found partition: " << *iter;
+                partition_names->erase(iter);
+                if (partition_names->empty()) {
+                    return COLDBOOT_STOP;  // found all partitions, stop coldboot
+                } else {
+                    return COLDBOOT_CREATE;  // create this device and continue to find others
+                }
+            }
+        }
+        // Not found a partition or find an unneeded partition, continue to find others
+        return COLDBOOT_CONTINUE;
+    });
+}
+
+static bool get_early_partitions(const std::vector<fstab_rec*>& early_fstab_recs,
+                                 std::set<std::string>* out_partitions, bool* out_need_verity) {
+    std::string meta_partition;
+    out_partitions->clear();
+    *out_need_verity = false;
+
+    for (auto fstab_rec : early_fstab_recs) {
+        // don't allow verifyatboot for early mounted partitions
+        if (fs_mgr_is_verifyatboot(fstab_rec)) {
+            LOG(ERROR) << "early_mount: partitions can't be verified at boot";
+            return false;
+        }
+        // check for verified partitions
+        if (fs_mgr_is_verified(fstab_rec)) {
+            *out_need_verity = true;
+        }
+        // check if verity metadata is on a separate partition and get partition
+        // name from the end of the ->verity_loc path. verity state is not partition
+        // specific, so there must be only 1 additional partition that carries
+        // verity state.
+        if (fstab_rec->verity_loc) {
+            if (!meta_partition.empty()) {
+                LOG(ERROR) << "early_mount: more than one meta partition found: " << meta_partition
+                           << ", " << basename(fstab_rec->verity_loc);
+                return false;
+            } else {
+                meta_partition = basename(fstab_rec->verity_loc);
+            }
         }
     }
-    early_device_socket_close();
+
+    // includes those early mount partitions and meta_partition (if any)
+    // note that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used
+    for (auto fstab_rec : early_fstab_recs) {
+        out_partitions->emplace(basename(fstab_rec->blk_device));
+    }
+
+    if (!meta_partition.empty()) {
+        out_partitions->emplace(std::move(meta_partition));
+    }
+
+    return true;
+}
+
+/* Early mount vendor and ODM partitions. The fstab is read from device-tree. */
+static bool early_mount() {
+    // skip early mount if we're in recovery mode
+    if (access("/sbin/recovery", F_OK) == 0) {
+        LOG(INFO) << "Early mount skipped (recovery mode)";
+        return true;
+    }
+
+    // first check if device tree fstab entries are compatible
+    if (!is_dt_fstab_compatible()) {
+        LOG(INFO) << "Early mount skipped (missing/incompatible fstab in device tree)";
+        return true;
+    }
+
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> tab(
+        fs_mgr_read_fstab_dt(), fs_mgr_free_fstab);
+    if (!tab) {
+        LOG(ERROR) << "Early mount failed to read fstab from device tree";
+        return false;
+    }
+
+    // find out fstab records for odm, system and vendor
+    std::vector<fstab_rec*> early_fstab_recs;
+    for (auto mount_point : {"/odm", "/system", "/vendor"}) {
+        fstab_rec* fstab_rec = fs_mgr_get_entry_for_mount_point(tab.get(), mount_point);
+        if (fstab_rec != nullptr) {
+            early_fstab_recs.push_back(fstab_rec);
+        }
+    }
+
+    // nothing to early mount
+    if (early_fstab_recs.empty()) return true;
+
+    bool need_verity;
+    std::set<std::string> partition_names;
+    // partition_names MUST have A/B suffix when A/B is used
+    if (!get_early_partitions(early_fstab_recs, &partition_names, &need_verity)) {
+        return false;
+    }
+
+    bool success = false;
+    // create the devices we need..
+    early_device_init(&partition_names);
+
+    // early_device_init will remove found partitions from partition_names
+    // So if the partition_names is not empty here, means some partitions
+    // are not found
+    if (!partition_names.empty()) {
+        LOG(ERROR) << "early_mount: partition(s) not found: "
+                   << android::base::Join(partition_names, ", ");
+        goto done;
+    }
+
+    if (need_verity) {
+        // create /dev/device mapper
+        device_init("/sys/devices/virtual/misc/device-mapper",
+                    [&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
+    }
+
+    for (auto fstab_rec : early_fstab_recs) {
+        if (!early_mount_one(fstab_rec)) goto done;
+    }
+    success = true;
+
+done:
+    device_close();
+    return success;
 }
 
 int main(int argc, char** argv) {
@@ -717,8 +1077,10 @@
     LOG(INFO) << "init " << (is_first_stage ? "first" : "second") << " stage started!";
 
     if (is_first_stage) {
-        // Mount devices defined in android.early.* kernel commandline
-        early_mount();
+        if (!early_mount()) {
+            LOG(ERROR) << "Failed to mount required partitions early ...";
+            panic();
+        }
 
         // Set up SELinux, loading the SELinux policy.
         selinux_initialize(true);
@@ -732,8 +1094,9 @@
 
         setenv("INIT_SECOND_STAGE", "true", 1);
 
-        uint64_t start_ns = start_time.time_since_epoch().count();
-        setenv("INIT_STARTED_AT", StringPrintf("%" PRIu64, start_ns).c_str(), 1);
+        static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
+        uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
+        setenv("INIT_STARTED_AT", StringPrintf("%" PRIu64, start_ms).c_str(), 1);
 
         char* path = argv[0];
         char* args[] = { path, nullptr };
@@ -760,6 +1123,9 @@
         property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
         property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
 
+        // Set libavb version for Framework-only OTA match in Treble build.
+        property_set("ro.boot.init.avb_version", std::to_string(AVB_MAJOR_VERSION).c_str());
+
         // Clean up our environment.
         unsetenv("INIT_SECOND_STAGE");
         unsetenv("INIT_STARTED_AT");
@@ -779,7 +1145,8 @@
     restorecon("/dev/random");
     restorecon("/dev/urandom");
     restorecon("/dev/__properties__");
-    restorecon("/property_contexts");
+    restorecon("/plat_property_contexts");
+    restorecon("/nonplat_property_contexts");
     restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
     restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
     restorecon("/dev/device-mapper");
@@ -804,7 +1171,20 @@
     parser.AddSectionParser("service",std::make_unique<ServiceParser>());
     parser.AddSectionParser("on", std::make_unique<ActionParser>());
     parser.AddSectionParser("import", std::make_unique<ImportParser>());
-    parser.ParseConfig("/init.rc");
+    std::string bootscript = property_get("ro.boot.init_rc");
+    if (bootscript.empty()) {
+        parser.ParseConfig("/init.rc");
+        parser.set_is_system_etc_init_loaded(
+                parser.ParseConfig("/system/etc/init"));
+        parser.set_is_vendor_etc_init_loaded(
+                parser.ParseConfig("/vendor/etc/init"));
+        parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
+    } else {
+        parser.ParseConfig(bootscript);
+        parser.set_is_system_etc_init_loaded(true);
+        parser.set_is_vendor_etc_init_loaded(true);
+        parser.set_is_odm_etc_init_loaded(true);
+    }
 
     ActionManager& am = ActionManager::GetInstance();
 
@@ -815,6 +1195,7 @@
     // ... so that we can start queuing up actions that require stuff from /dev.
     am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
     am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
+    am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
     am.QueueBuiltinAction(keychord_init_action, "keychord_init");
     am.QueueBuiltinAction(console_init_action, "console_init");
 
@@ -837,7 +1218,7 @@
     am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
 
     while (true) {
-        if (!waiting_for_exec) {
+        if (!(waiting_for_exec || waiting_for_prop)) {
             am.ExecuteOneCommand();
             restart_processes();
         }
@@ -854,8 +1235,6 @@
         // If there's more work to do, wake up again immediately.
         if (am.HasMoreCommands()) epoll_timeout_ms = 0;
 
-        bootchart_sample(&epoll_timeout_ms);
-
         epoll_event ev;
         int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
         if (nr == -1) {
diff --git a/init/init.h b/init/init.h
index cfb3139..3768c02 100644
--- a/init/init.h
+++ b/init/init.h
@@ -23,7 +23,6 @@
 class Service;
 
 extern const char *ENV[32];
-extern bool waiting_for_exec;
 extern std::string default_console;
 extern struct selabel_handle *sehandle;
 extern struct selabel_handle *sehandle_prop;
@@ -36,4 +35,10 @@
 
 int add_environment(const char* key, const char* val);
 
+bool start_waiting_for_exec();
+
+void stop_waiting_for_exec();
+
+bool start_waiting_for_property(const char *name, const char *value);
+
 #endif  /* _INIT_INIT_H */
diff --git a/init/init_parser.h b/init/init_parser.h
index 5ed30ad..f66ba52 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -41,6 +41,18 @@
     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;
+    }
+    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();
@@ -50,6 +62,9 @@
     bool ParseConfigDir(const std::string& path);
 
     std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
+    bool is_system_etc_init_loaded_ = false;
+    bool is_vendor_etc_init_loaded_ = false;
+    bool is_odm_etc_init_loaded_ = false;
 };
 
 #endif
diff --git a/init/log.cpp b/init/log.cpp
index 8618340..6b32526 100644
--- a/init/log.cpp
+++ b/init/log.cpp
@@ -19,6 +19,8 @@
 #include <fcntl.h>
 #include <string.h>
 
+#include <linux/audit.h>
+#include <netlink/netlink.h>
 #include <selinux/selinux.h>
 
 void InitKernelLogging(char* argv[]) {
@@ -38,6 +40,24 @@
     android::base::InitLogging(argv, &android::base::KernelLogger);
 }
 
+static void selinux_avc_log(char* buf, size_t buf_len) {
+    size_t str_len = strnlen(buf, buf_len);
+
+    // trim newline at end of string
+    buf[str_len - 1] = '\0';
+
+    struct nl_sock* sk = nl_socket_alloc();
+    if (sk == NULL) {
+        return;
+    }
+    nl_connect(sk, NETLINK_AUDIT);
+    int result;
+    do {
+        result = nl_send_simple(sk, AUDIT_USER_AVC, 0, buf, str_len);
+    } while (result == -NLE_INTR);
+    nl_socket_free(sk);
+}
+
 int selinux_klog_callback(int type, const char *fmt, ...) {
     android::base::LogSeverity severity = android::base::ERROR;
     if (type == SELINUX_WARNING) {
@@ -48,8 +68,15 @@
     char buf[1024];
     va_list ap;
     va_start(ap, fmt);
-    vsnprintf(buf, sizeof(buf), fmt, ap);
+    int res = vsnprintf(buf, sizeof(buf), fmt, ap);
     va_end(ap);
-    android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
+    if (res <= 0) {
+        return 0;
+    }
+    if (type == SELINUX_AVC) {
+        selinux_avc_log(buf, sizeof(buf));
+    } else {
+        android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
+    }
     return 0;
 }
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 498a5a1..d88b72e 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -27,6 +28,7 @@
 #include <sys/poll.h>
 
 #include <memory>
+#include <vector>
 
 #include <cutils/misc.h>
 #include <cutils/sockets.h>
@@ -48,6 +50,8 @@
 
 #include <fs_mgr.h>
 #include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include "bootimg.h"
 
 #include "property_service.h"
@@ -55,6 +59,8 @@
 #include "util.h"
 #include "log.h"
 
+using android::base::StringPrintf;
+
 #define PERSISTENT_PROPERTY_DIR  "/data/property"
 #define FSTAB_PREFIX "/fstab."
 #define RECOVERY_MOUNT_POINT "/recovery"
@@ -70,30 +76,30 @@
     }
 }
 
-static int check_mac_perms(const char *name, char *sctx, struct ucred *cr)
-{
-    char *tctx = NULL;
-    int result = 0;
+static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* cr) {
+
+    if (!sctx) {
+      return false;
+    }
+
+    if (!sehandle_prop) {
+      return false;
+    }
+
+    char* tctx = nullptr;
+    if (selabel_lookup(sehandle_prop, &tctx, name.c_str(), 1) != 0) {
+      return false;
+    }
+
     property_audit_data audit_data;
 
-    if (!sctx)
-        goto err;
-
-    if (!sehandle_prop)
-        goto err;
-
-    if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0)
-        goto err;
-
-    audit_data.name = name;
+    audit_data.name = name.c_str();
     audit_data.cr = cr;
 
-    if (selinux_check_access(sctx, tctx, "property_service", "set", reinterpret_cast<void*>(&audit_data)) == 0)
-        result = 1;
+    bool has_access = (selinux_check_access(sctx, tctx, "property_service", "set", &audit_data) == 0);
 
     freecon(tctx);
- err:
-    return result;
+    return has_access;
 }
 
 static int check_control_mac_perms(const char *name, char *sctx, struct ucred *cr)
@@ -142,11 +148,9 @@
     }
 }
 
-bool is_legal_property_name(const std::string &name)
-{
+bool is_legal_property_name(const std::string& name) {
     size_t namelen = name.size();
 
-    if (namelen >= PROP_NAME_MAX) return false;
     if (namelen < 1) return false;
     if (name[0] == '.') return false;
     if (name[namelen - 1] == '.') return false;
@@ -169,139 +173,283 @@
     return true;
 }
 
-int property_set(const char* name, const char* value) {
-    size_t valuelen = strlen(value);
+uint32_t property_set(const std::string& name, const std::string& value) {
+    size_t valuelen = value.size();
 
     if (!is_legal_property_name(name)) {
         LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";
-        return -1;
+        return PROP_ERROR_INVALID_NAME;
     }
+
     if (valuelen >= PROP_VALUE_MAX) {
         LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
                    << "value too long";
-        return -1;
+        return PROP_ERROR_INVALID_VALUE;
     }
 
-    if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
-        if (restorecon(value, SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+    if (name == "selinux.restorecon_recursive" && valuelen > 0) {
+        if (restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
             LOG(ERROR) << "Failed to restorecon_recursive " << value;
         }
     }
 
-    prop_info* pi = (prop_info*) __system_property_find(name);
+    prop_info* pi = (prop_info*) __system_property_find(name.c_str());
     if (pi != nullptr) {
         // ro.* properties are actually "write-once".
-        if (!strncmp(name, "ro.", 3)) {
+        if (android::base::StartsWith(name, "ro.")) {
             LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
                        << "property already set";
-            return -1;
+            return PROP_ERROR_READ_ONLY_PROPERTY;
         }
 
-        __system_property_update(pi, value, valuelen);
+        __system_property_update(pi, value.c_str(), valuelen);
     } else {
-        int rc = __system_property_add(name, strlen(name), value, valuelen);
+        int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
         if (rc < 0) {
             LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
                        << "__system_property_add failed";
-            return rc;
+            return PROP_ERROR_SET_FAILED;
         }
     }
 
     // Don't write properties to disk until after we have read all default
     // properties to prevent them from being overwritten by default values.
-    if (persistent_properties_loaded && strncmp("persist.", name, strlen("persist.")) == 0) {
-        write_persistent_property(name, value);
+    if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
+        write_persistent_property(name.c_str(), value.c_str());
     }
-    property_changed(name, value);
-    return 0;
+    property_changed(name.c_str(), value.c_str());
+    return PROP_SUCCESS;
 }
 
-static void handle_property_set_fd()
-{
-    prop_msg msg;
-    int r;
-    char * source_ctx = NULL;
+class SocketConnection {
+ public:
+  SocketConnection(int socket, const struct ucred& cred)
+      : socket_(socket), cred_(cred) {}
 
-    int s = accept(property_set_fd, nullptr, nullptr);
+  ~SocketConnection() {
+    close(socket_);
+  }
+
+  bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) {
+    return RecvFully(value, sizeof(*value), timeout_ms);
+  }
+
+  bool RecvChars(char* chars, size_t size, uint32_t* timeout_ms) {
+    return RecvFully(chars, size, timeout_ms);
+  }
+
+  bool RecvString(std::string* value, uint32_t* timeout_ms) {
+    uint32_t len = 0;
+    if (!RecvUint32(&len, timeout_ms)) {
+      return false;
+    }
+
+    if (len == 0) {
+      *value = "";
+      return true;
+    }
+
+    // http://b/35166374: don't allow init to make arbitrarily large allocations.
+    if (len > 0xffff) {
+      LOG(ERROR) << "sys_prop: RecvString asked to read huge string: " << len;
+      errno = ENOMEM;
+      return false;
+    }
+
+    std::vector<char> chars(len);
+    if (!RecvChars(&chars[0], len, timeout_ms)) {
+      return false;
+    }
+
+    *value = std::string(&chars[0], len);
+    return true;
+  }
+
+  bool SendUint32(uint32_t value) {
+    int result = TEMP_FAILURE_RETRY(send(socket_, &value, sizeof(value), 0));
+    return result == sizeof(value);
+  }
+
+  int socket() {
+    return socket_;
+  }
+
+  const struct ucred& cred() {
+    return cred_;
+  }
+
+ private:
+  bool PollIn(uint32_t* timeout_ms) {
+    struct pollfd ufds[1];
+    ufds[0].fd = socket_;
+    ufds[0].events = POLLIN;
+    ufds[0].revents = 0;
+    while (*timeout_ms > 0) {
+      Timer timer;
+      int nr = poll(ufds, 1, *timeout_ms);
+      uint64_t millis = timer.duration_ms();
+      *timeout_ms = (millis > *timeout_ms) ? 0 : *timeout_ms - millis;
+
+      if (nr > 0) {
+        return true;
+      }
+
+      if (nr == 0) {
+        // Timeout
+        break;
+      }
+
+      if (nr < 0 && errno != EINTR) {
+        PLOG(ERROR) << "sys_prop: error waiting for uid " << cred_.uid << " to send property message";
+        return false;
+      } else { // errno == EINTR
+        // Timer rounds milliseconds down in case of EINTR we want it to be rounded up
+        // to avoid slowing init down by causing EINTR with under millisecond timeout.
+        if (*timeout_ms > 0) {
+          --(*timeout_ms);
+        }
+      }
+    }
+
+    LOG(ERROR) << "sys_prop: timeout waiting for uid " << cred_.uid << " to send property message.";
+    return false;
+  }
+
+  bool RecvFully(void* data_ptr, size_t size, uint32_t* timeout_ms) {
+    size_t bytes_left = size;
+    char* data = static_cast<char*>(data_ptr);
+    while (*timeout_ms > 0 && bytes_left > 0) {
+      if (!PollIn(timeout_ms)) {
+        return false;
+      }
+
+      int result = TEMP_FAILURE_RETRY(recv(socket_, data, bytes_left, MSG_DONTWAIT));
+      if (result <= 0) {
+        return false;
+      }
+
+      bytes_left -= result;
+      data += result;
+    }
+
+    return bytes_left == 0;
+  }
+
+  int socket_;
+  struct ucred cred_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
+};
+
+static void handle_property_set(SocketConnection& socket,
+                                const std::string& name,
+                                const std::string& value,
+                                bool legacy_protocol) {
+  const char* cmd_name = legacy_protocol ? "PROP_MSG_SETPROP" : "PROP_MSG_SETPROP2";
+  if (!is_legal_property_name(name)) {
+    LOG(ERROR) << "sys_prop(" << cmd_name << "): illegal property name \"" << name << "\"";
+    socket.SendUint32(PROP_ERROR_INVALID_NAME);
+    return;
+  }
+
+  struct ucred cr = socket.cred();
+  char* source_ctx = nullptr;
+  getpeercon(socket.socket(), &source_ctx);
+
+  if (android::base::StartsWith(name, "ctl.")) {
+    if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
+      handle_control_message(name.c_str() + 4, value.c_str());
+      if (!legacy_protocol) {
+        socket.SendUint32(PROP_SUCCESS);
+      }
+    } else {
+      LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
+                 << " service ctl [" << value << "]"
+                 << " uid:" << cr.uid
+                 << " gid:" << cr.gid
+                 << " pid:" << cr.pid;
+      if (!legacy_protocol) {
+        socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
+      }
+    }
+  } else {
+    if (check_mac_perms(name, source_ctx, &cr)) {
+      uint32_t result = property_set(name, value);
+      if (!legacy_protocol) {
+        socket.SendUint32(result);
+      }
+    } else {
+      LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name;
+      if (!legacy_protocol) {
+        socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
+      }
+    }
+  }
+
+  freecon(source_ctx);
+}
+
+static void handle_property_set_fd() {
+    static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
+
+    int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
     if (s == -1) {
         return;
     }
 
-    /* Check socket options here */
     struct ucred cr;
     socklen_t cr_size = sizeof(cr);
     if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
         close(s);
-        PLOG(ERROR) << "Unable to receive socket options";
+        PLOG(ERROR) << "sys_prop: unable to get SO_PEERCRED";
         return;
     }
 
-    static constexpr int timeout_ms = 2 * 1000;  /* Default 2 sec timeout for caller to send property. */
-    struct pollfd ufds[1];
-    ufds[0].fd = s;
-    ufds[0].events = POLLIN;
-    ufds[0].revents = 0;
-    int nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms));
-    if (nr == 0) {
-        LOG(ERROR) << "sys_prop: timeout waiting for uid " << cr.uid << " to send property message.";
-        close(s);
-        return;
-    } else if (nr < 0) {
-        PLOG(ERROR) << "sys_prop: error waiting for uid " << cr.uid << " to send property message";
-        close(s);
+    SocketConnection socket(s, cr);
+    uint32_t timeout_ms = kDefaultSocketTimeout;
+
+    uint32_t cmd = 0;
+    if (!socket.RecvUint32(&cmd, &timeout_ms)) {
+        PLOG(ERROR) << "sys_prop: error while reading command from the socket";
+        socket.SendUint32(PROP_ERROR_READ_CMD);
         return;
     }
 
-    r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
-    if(r != sizeof(prop_msg)) {
-        PLOG(ERROR) << "sys_prop: mis-match msg size received: " << r << " expected: " << sizeof(prop_msg);
-        close(s);
-        return;
-    }
+    switch (cmd) {
+    case PROP_MSG_SETPROP: {
+        char prop_name[PROP_NAME_MAX];
+        char prop_value[PROP_VALUE_MAX];
 
-    switch(msg.cmd) {
-    case PROP_MSG_SETPROP:
-        msg.name[PROP_NAME_MAX-1] = 0;
-        msg.value[PROP_VALUE_MAX-1] = 0;
-
-        if (!is_legal_property_name(msg.name)) {
-            LOG(ERROR) << "sys_prop: illegal property name \"" << msg.name << "\"";
-            close(s);
-            return;
+        if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
+            !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
+          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
+          return;
         }
 
-        getpeercon(s, &source_ctx);
+        prop_name[PROP_NAME_MAX-1] = 0;
+        prop_value[PROP_VALUE_MAX-1] = 0;
 
-        if(memcmp(msg.name,"ctl.",4) == 0) {
-            // Keep the old close-socket-early behavior when handling
-            // ctl.* properties.
-            close(s);
-            if (check_control_mac_perms(msg.value, source_ctx, &cr)) {
-                handle_control_message((char*) msg.name + 4, (char*) msg.value);
-            } else {
-                LOG(ERROR) << "sys_prop: Unable to " << (msg.name + 4)
-                           << " service ctl [" << msg.value << "]"
-                           << " uid:" << cr.uid
-                           << " gid:" << cr.gid
-                           << " pid:" << cr.pid;
-            }
-        } else {
-            if (check_mac_perms(msg.name, source_ctx, &cr)) {
-                property_set((char*) msg.name, (char*) msg.value);
-            } else {
-                LOG(ERROR) << "sys_prop: permission denied uid:" << cr.uid << " name:" << msg.name;
-            }
-
-            // Note: bionic's property client code assumes that the
-            // property server will not close the socket until *AFTER*
-            // the property is written to memory.
-            close(s);
-        }
-        freecon(source_ctx);
+        handle_property_set(socket, prop_value, prop_value, true);
         break;
+      }
+
+    case PROP_MSG_SETPROP2: {
+        std::string name;
+        std::string value;
+        if (!socket.RecvString(&name, &timeout_ms) ||
+            !socket.RecvString(&value, &timeout_ms)) {
+          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP2): error while reading name/value from the socket";
+          socket.SendUint32(PROP_ERROR_READ_DATA);
+          return;
+        }
+
+        handle_property_set(socket, name, value, false);
+        break;
+      }
 
     default:
-        close(s);
+        LOG(ERROR) << "sys_prop: invalid command " << cmd;
+        socket.SendUint32(PROP_ERROR_INVALID_CMD);
         break;
     }
 }
@@ -438,14 +586,16 @@
 }
 
 void property_load_boot_defaults() {
-    load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
+    load_properties_from_file("/default.prop", NULL);
+    load_properties_from_file("/odm/default.prop", NULL);
+    load_properties_from_file("/vendor/default.prop", NULL);
 }
 
 static void load_override_properties() {
     if (ALLOW_LOCAL_PROP_OVERRIDE) {
         std::string debuggable = property_get("ro.debuggable");
         if (debuggable == "1") {
-            load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE, NULL);
+            load_properties_from_file("/data/local.prop", NULL);
         }
     }
 }
@@ -459,6 +609,7 @@
     load_override_properties();
     /* Read persistent properties after all default values have been loaded. */
     load_persistent_properties();
+    property_set("ro.persistent_properties.ready", "true");
 }
 
 void load_recovery_id_prop() {
@@ -500,13 +651,16 @@
 }
 
 void load_system_props() {
-    load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
-    load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);
-    load_properties_from_file(PROP_PATH_FACTORY, "ro.*");
+    load_properties_from_file("/system/build.prop", NULL);
+    load_properties_from_file("/odm/build.prop", NULL);
+    load_properties_from_file("/vendor/build.prop", NULL);
+    load_properties_from_file("/factory/factory.prop", "ro.*");
     load_recovery_id_prop();
 }
 
 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);
     if (property_set_fd == -1) {
diff --git a/init/property_service.h b/init/property_service.h
index e3a2acb..5d59473 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -27,14 +27,14 @@
     const char* name;
 };
 
-extern void property_init(void);
-extern void property_load_boot_defaults(void);
-extern void load_persist_props(void);
-extern void load_system_props(void);
-extern void start_property_service(void);
+void property_init(void);
+void property_load_boot_defaults(void);
+void load_persist_props(void);
+void load_system_props(void);
+void start_property_service(void);
 std::string property_get(const char* name);
-extern int property_set(const char *name, const char *value);
-extern bool is_legal_property_name(const std::string &name);
+uint32_t property_set(const std::string& name, const std::string& value);
+bool is_legal_property_name(const std::string& name);
 
 
 #endif  /* _INIT_PROPERTY_H */
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
new file mode 100644
index 0000000..4d784aa
--- /dev/null
+++ b/init/property_service_test.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+#include <gtest/gtest.h>
+
+TEST(property_service, very_long_name_35166374) {
+  // Connect to the property service directly...
+  int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+  ASSERT_NE(fd, -1);
+
+  static const char* property_service_socket = "/dev/socket/" PROP_SERVICE_NAME;
+  sockaddr_un addr = {};
+  addr.sun_family = AF_LOCAL;
+  strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));
+
+  socklen_t addr_len = strlen(property_service_socket) + offsetof(sockaddr_un, sun_path) + 1;
+  ASSERT_NE(connect(fd, reinterpret_cast<sockaddr*>(&addr), addr_len), -1);
+
+  // ...so we can send it a malformed request.
+  uint32_t msg = PROP_MSG_SETPROP2;
+  uint32_t size = 0xffffffff;
+  uint32_t data = 0xdeadbeef;
+
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(msg)), send(fd, &msg, sizeof(msg), 0));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(size)), send(fd, &size, sizeof(size), 0));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(data)), send(fd, &data, sizeof(data), 0));
+  ASSERT_EQ(0, close(fd));
+}
diff --git a/init/readme.txt b/init/readme.txt
deleted file mode 100644
index 6f40d6b..0000000
--- a/init/readme.txt
+++ /dev/null
@@ -1,567 +0,0 @@
-Android Init Language
----------------------
-
-The Android Init Language consists of five broad classes of statements,
-which are Actions, Commands, Services, Options, and Imports.
-
-All of these are line-oriented, consisting of tokens separated by
-whitespace.  The c-style backslash escapes may be used to insert
-whitespace into a token.  Double quotes may also be used to prevent
-whitespace from breaking text into multiple tokens.  The backslash,
-when it is the last character on a line, may be used for line-folding.
-
-Lines which start with a # (leading whitespace allowed) are comments.
-
-Actions and Services implicitly declare a new section.  All commands
-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
-with the same name as an existing one, it is ignored and an error
-message is logged.
-
-
-Init .rc Files
---------------
-The init language is used in plaintext files that take the .rc file
-extension.  These are typically multiple of these in multiple
-locations on the system, described below.
-
-/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.
-
-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.
-This is primarily for supporting factory mode and other non-standard boot
-modes.  The three default paths should be used for the normal boot process.
-
-The intention of these directories is as follows
-   1) /system/etc/init/ is for core system items such as
-      SurfaceFlinger, MediaService, and logcatd.
-   2) /vendor/etc/init/ is for SoC vendor items such as actions or
-      daemons needed for core SoC functionality.
-   3) /odm/etc/init/ is for device manufacturer items such as
-      actions or daemons needed for motion sensor or other peripheral
-      functionality.
-
-All services whose binaries reside on the system, vendor, or odm
-partitions should have their service entries placed into a
-corresponding init .rc file, located in the /etc/init/
-directory of the partition where they reside.  There is a build
-system macro, LOCAL_INIT_RC, that handles this for developers.  Each
-init .rc file should additionally contain any actions associated with
-its service.
-
-An example is the logcatd.rc and Android.mk files located in the
-system/core/logcat directory.  The LOCAL_INIT_RC macro in the
-Android.mk file places logcatd.rc in /system/etc/init/ during the
-build process.  Init loads logcatd.rc during the mount_all command and
-allows the service to be run and the action to be queued when
-appropriate.
-
-This break up of init .rc files according to their daemon is preferred
-to the previously used monolithic init .rc files.  This approach
-ensures that the only service entries that init reads and the only
-actions that init performs correspond to services whose binaries are in
-fact present on the file system, which was not the case with the
-monolithic init .rc files.  This additionally will aid in merge
-conflict resolution when multiple services are added to the system, as
-each one will go into a separate file.
-
-There are two options "early" and "late" in mount_all command
-which can be set after optional paths. With "--early" set, the
-init executable will skip mounting entries with "latemount" flag
-and triggering fs encryption state event. With "--late" set,
-init executable will only mount entries with "latemount" flag but skip
-importing rc files. By default, no option is set, and mount_all will
-mount_all will process all entries in the given fstab.
-
-Actions
--------
-Actions are named sequences of commands.  Actions have a trigger which
-is used to determine when the action should occur.  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).
-
-Each action in the queue is dequeued in sequence and each command in
-that action is executed in sequence.  Init handles other activities
-(device creation/destruction, property setting, process restarting)
-"between" the execution of the commands in activities.
-
-Actions take the form of:
-
-on <trigger> [&& <trigger>]*
-   <command>
-   <command>
-   <command>
-
-
-Services
---------
-Services are programs which init launches and (optionally) restarts
-when they exit.  Services take the form of:
-
-service <name> <pathname> [ <argument> ]*
-   <option>
-   <option>
-   ...
-
-
-Options
--------
-Options are modifiers to services.  They affect how and when init
-runs the service.
-
-console [<console>]
-  This service needs a console. The optional second parameter chooses a
-  specific console instead of the default. The default "/dev/console" can
-  be changed by setting the "androidboot.console" kernel parameter. In
-  all cases the leading "/dev/" should be omitted, so "/dev/tty0" would be
-  specified as just "console tty0".
-
-critical
-  This is a device-critical service. If it exits more than four times in
-  four minutes, the device will reboot into recovery mode.
-
-disabled
-  This service will not automatically start with its class.
-  It must be explicitly started by name.
-
-setenv <name> <value>
-  Set the environment variable <name> to <value> in the launched process.
-
-socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
-  Create a unix domain socket named /dev/socket/<name> and pass its fd to the
-  launched process.  <type> must be "dgram", "stream" or "seqpacket".  User and
-  group default to 0.  'seclabel' is the SELinux security context for the
-  socket.  It defaults to the service security context, as specified by
-  seclabel or computed based on the service executable file security context.
-  For native executables see libcutils android_get_control_socket().
-
-file <path> <type>
-  Open a file path and pass its fd to the launched process.  <type> must be
-  "r", "w" or "rw".  For native executables see libcutils
-  android_get_control_file().
-
-user <username>
-  Change to 'username' before exec'ing this service.
-  Currently defaults to root.  (??? probably should default to nobody)
-  As of Android M, processes should use this option even if they
-  require Linux capabilities.  Previously, to acquire Linux
-  capabilities, a process would need to run as root, request the
-  capabilities, then drop to its desired uid.  There is a new
-  mechanism through fs_config that allows device manufacturers to add
-  Linux capabilities to specific binaries on a file system that should
-  be used instead. This mechanism is described on
-  http://source.android.com/devices/tech/config/filesystem.html.  When
-  using this new mechanism, processes can use the user option to
-  select their desired uid without ever running as root.
-  As of Android O, processes can also request capabilities directly in their .rc
-  files. See the "capabilities" option below.
-
-group <groupname> [ <groupname> ]*
-  Change to 'groupname' before exec'ing this service.  Additional
-  groupnames beyond the (required) first one are used to set the
-  supplemental groups of the process (via setgroups()).
-  Currently defaults to root.  (??? probably should default to nobody)
-
-capabilities <capability> [ <capability> ]*
-  Set capabilities when exec'ing this service. 'capability' should be a Linux
-  capability without the "CAP_" prefix, like "NET_ADMIN" or "SETPCAP". See
-  http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux
-  capabilities.
-
-seclabel <seclabel>
-  Change to 'seclabel' before exec'ing this service.
-  Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
-  Services on the system partition can instead use policy-defined transitions
-  based on their file security context.
-  If not specified and no transition is defined in policy, defaults to the init context.
-
-oneshot
-  Do not restart the service when it exits.
-
-class <name>
-  Specify a class name for the service.  All services in a
-  named class may be started or stopped together.  A service
-  is in the class "default" if one is not specified via the
-  class option.
-
-onrestart
-  Execute a Command (see below) when service restarts.
-
-writepid <file...>
-  Write the child's pid to the given files when it forks. Meant for
-  cgroup/cpuset usage.
-
-priority <priority>
-  Scheduling priority of the service process. This value has to be in range
-  -20 to 19. Default priority is 0. Priority is set via setpriority().
-
-namespace <pid|mnt>
-  Enter a new PID or mount namespace when forking the service.
-
-oom_score_adjust <value>
-   Sets the child's /proc/self/oom_score_adj to the specified value,
-   which must range from -1000 to 1000.
-
-
-Triggers
---------
-Triggers are strings which can be used to match certain kinds of
-events and used to cause an action to occur.
-
-Triggers are subdivided into event triggers and property triggers.
-
-Event triggers are strings triggered by the 'trigger' command or by
-the QueueEventTrigger() function within the init executable.  These
-take the form of a simple string such as 'boot' or 'late-init'.
-
-Property triggers are strings triggered when a named property changes
-value to a given new value or when a named property changes value to
-any new value.  These take the form of 'property:<name>=<value>' and
-'property:<name>=*' respectively.  Property triggers are additionally
-evaluated and triggered accordingly during the initial boot phase of
-init.
-
-An Action can have multiple property triggers but may only have one
-event trigger.
-
-For example:
-'on boot && property:a=b' defines an action that is only executed when
-the 'boot' event trigger happens and the property a equals b.
-
-'on property:a=b && property:c=d' defines an action that is executed
-at three times,
-   1) During initial boot if property a=b and property c=d
-   2) Any time that property a transitions to value b, while property
-      c already equals d.
-   3) Any time that property c transitions to value d, while property
-      a already equals b.
-
-
-Commands
---------
-
-bootchart_init
-   Start bootcharting if configured (see below).
-   This is included in the default init.rc.
-
-chmod <octal-mode> <path>
-   Change file access permissions.
-
-chown <owner> <group> <path>
-   Change file owner and group.
-
-class_start <serviceclass>
-   Start all services of the specified class if they are
-   not already running.
-
-class_stop <serviceclass>
-   Stop and disable all services of the specified class if they are
-   currently running.
-
-class_reset <serviceclass>
-   Stop all services of the specified class if they are
-   currently running, without disabling them. They can be restarted
-   later using class_start.
-
-copy <src> <dst>
-   Copies a file. Similar to write, but useful for binary/large
-   amounts of data.
-
-domainname <name>
-   Set the domain name.
-
-enable <servicename>
-   Turns a disabled service into an enabled one as if the service did not
-   specify disabled.
-   If the service is supposed to be running, it will be started now.
-   Typically used when the bootloader sets a variable that indicates a specific
-   service should be started when needed. E.g.
-     on property:ro.boot.myfancyhardware=1
-        enable my_fancy_service_for_my_fancy_hardware
-
-exec [ <seclabel> [ <user> [ <group> ]* ] ] -- <command> [ <argument> ]*
-   Fork and execute command with the given arguments. The command starts
-   after "--" so that an optional security context, user, and supplementary
-   groups can be provided. No other commands will be run until this one
-   finishes. <seclabel> can be a - to denote default.
-
-export <name> <value>
-   Set the environment variable <name> equal to <value> in the
-   global environment (which will be inherited by all processes
-   started after this command is executed)
-
-hostname <name>
-   Set the host name.
-
-ifup <interface>
-   Bring the network interface <interface> online.
-
-insmod [-f] <path> [<options>]
-   Install the module at <path> with the specified options.
-   -f
-   Force installation of the module even if the version of the running kernel
-   and the version of the kernel for which the module was compiled do not match.
-
-load_all_props
-   Loads properties from /system, /vendor, et cetera.
-   This is included in the default init.rc.
-
-load_persist_props
-   Loads persistent properties when /data has been decrypted.
-   This is included in the default init.rc.
-
-loglevel <level>
-   Sets the kernel log level to level. Properties are expanded within <level>.
-
-mkdir <path> [mode] [owner] [group]
-   Create a directory at <path>, optionally with the given mode, owner, and
-   group. If not provided, the directory is created with permissions 755 and
-   owned by the root user and root group. If provided, the mode, owner and group
-   will be updated if the directory exists already.
-
-mount_all <fstab> [ <path> ]* [--<option>]
-   Calls fs_mgr_mount_all on the given fs_mgr-format fstab and imports .rc files
-   at the specified paths (e.g., on the partitions just mounted) with optional
-   options "early" and "late".
-   Refer to the section of "Init .rc Files" for detail.
-
-mount <type> <device> <dir> [ <flag> ]* [<options>]
-   Attempt to mount the named device at the directory <dir>
-   <flag>s include "ro", "rw", "remount", "noatime", ...
-   <options> include "barrier=1", "noauto_da_alloc", "discard", ... as
-   a comma separated string, eg: barrier=1,noauto_da_alloc
-
-powerctl
-   Internal implementation detail used to respond to changes to the
-   "sys.powerctl" system property, used to implement rebooting.
-
-restart <service>
-   Like stop, but doesn't disable the service.
-
-restorecon <path> [ <path> ]*
-   Restore the file named by <path> to the security context specified
-   in the file_contexts configuration.
-   Not required for directories created by the init.rc as these are
-   automatically labeled correctly by init.
-
-restorecon_recursive <path> [ <path> ]*
-   Recursively restore the directory tree named by <path> to the
-   security contexts specified in the file_contexts configuration.
-
-rm <path>
-   Calls unlink(2) on the given path. You might want to
-   use "exec -- rm ..." instead (provided the system partition is
-   already mounted).
-
-rmdir <path>
-   Calls rmdir(2) on the given path.
-
-setprop <name> <value>
-   Set system property <name> to <value>. Properties are expanded
-   within <value>.
-
-setrlimit <resource> <cur> <max>
-   Set the rlimit for a resource.
-
-start <service>
-   Start a service running if it is not already running.
-
-stop <service>
-   Stop a service from running if it is currently running.
-
-swapon_all <fstab>
-   Calls fs_mgr_swapon_all on the given fstab file.
-
-symlink <target> <path>
-   Create a symbolic link at <path> with the value <target>
-
-sysclktz <mins_west_of_gmt>
-   Set the system clock base (0 if system clock ticks in GMT)
-
-trigger <event>
-   Trigger an event.  Used to queue an action from another
-   action.
-
-umount <path>
-   Unmount the filesystem mounted at that path.
-
-verity_load_state
-   Internal implementation detail used to load dm-verity state.
-
-verity_update_state <mount_point>
-   Internal implementation detail used to update dm-verity state and
-   set the partition.<mount_point>.verified properties used by adb remount
-   because fs_mgr can't set them directly itself.
-
-wait <path> [ <timeout> ]
-   Poll for the existence of the given file and return when found,
-   or the timeout has been reached. If timeout is not specified it
-   currently defaults to five seconds.
-
-write <path> <content>
-   Open the file at <path> and write a string to it with write(2).
-   If the file does not exist, it will be created. If it does exist,
-   it will be truncated. Properties are expanded within <content>.
-
-
-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,
-   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
-
-
-Properties
-----------
-Init provides information about the services that it is responsible
-for via the below properties.
-
-init.svc.<name>
-  State of a named service ("stopped", "stopping", "running", "restarting")
-
-
-Boot timing
------------
-Init records some boot timing information in system properties.
-
-ro.boottime.init
-  Time after boot in ns (via the CLOCK_BOOTTIME clock) at which the first
-  stage of init started.
-
-ro.boottime.init.selinux
-  How long it took the first stage to initialize SELinux.
-
-ro.boottime.init.cold_boot_wait
-  How long init waited for ueventd's coldboot phase to end.
-
-ro.boottime.<service-name>
-  Time after boot in ns (via the CLOCK_BOOTTIME clock) that the service was
-  first started.
-
-
-Bootcharting
-------------
-This version of init contains code to perform "bootcharting": generating log
-files that can be later processed by the tools provided by www.bootchart.org.
-
-On the emulator, use the -bootchart <timeout> option to boot with bootcharting
-activated for <timeout> seconds.
-
-On a device, create /data/bootchart/start with a command like the following:
-
-  adb shell 'echo $TIMEOUT > /data/bootchart/start'
-
-Where the value of $TIMEOUT corresponds to the desired bootcharted period in
-seconds. Bootcharting will stop after that many seconds have elapsed.
-You can also stop the bootcharting at any moment by doing the following:
-
-  adb shell 'echo 1 > /data/bootchart/stop'
-
-Note that /data/bootchart/stop is deleted automatically by init at the end of
-the bootcharting. This is not the case with /data/bootchart/start, so don't
-forget to delete it when you're done collecting data.
-
-The log files are written to /data/bootchart/. A script is provided to
-retrieve them and create a bootchart.tgz file that can be used with the
-bootchart command-line utility:
-
-  sudo apt-get install pybootchartgui
-  # grab-bootchart.sh uses $ANDROID_SERIAL.
-  $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
-
-One thing to watch for is that the bootchart will show init as if it started
-running at 0s. You'll have to look at dmesg to work out when the kernel
-actually started init.
-
-
-Comparing two bootcharts
-------------------------
-A handy script named compare-bootcharts.py can be used to compare the
-start/end time of selected processes. The aforementioned grab-bootchart.sh
-will leave a bootchart tarball named bootchart.tgz at /tmp/android-bootchart.
-If two such barballs are preserved on the host machine under different
-directories, the script can list the timestamps differences. For example:
-
-Usage: system/core/init/compare-bootcharts.py base_bootchart_dir
-       exp_bootchart_dir
-
-process: baseline experiment (delta)
- - Unit is ms (a jiffy is 10 ms on the system)
-------------------------------------
-/init: 50 40 (-10)
-/system/bin/surfaceflinger: 4320 4470 (+150)
-/system/bin/bootanimation: 6980 6990 (+10)
-zygote64: 10410 10640 (+230)
-zygote: 10410 10640 (+230)
-system_server: 15350 15150 (-200)
-bootanimation ends at: 33790 31230 (-2560)
-
-
-Systrace
---------
-Systrace [1] can be used for obtaining performance analysis reports during boot
-time on userdebug or eng builds.
-Here is an example of trace events of "wm" and "am" categories:
-
-  $ANDROID_BUILD_TOP/external/chromium-trace/systrace.py wm am --boot
-
-This command will cause the device to reboot. After the device is rebooted and
-the boot sequence has finished, the trace report is obtained from the device
-and written as trace.html on the host by hitting Ctrl+C.
-
-LIMITATION
-Recording trace events is started after persistent properties are loaded, so
-the trace events that are emitted before that are not recorded. Several
-services such as vold, surfaceflinger, and servicemanager are affected by this
-limitation since they are started before persistent properties are loaded.
-Zygote initialization and the processes that are forked from the zygote are not
-affected.
-
-[1] http://developer.android.com/tools/help/systrace.html
-
-
-Debugging init
---------------
-By default, programs executed by init will drop stdout and stderr into
-/dev/null. To help with debugging, you can execute your program via the
-Android program logwrapper. This will redirect stdout/stderr into the
-Android logging system (accessed via logcat).
-
-For example
-service akmd /system/bin/logwrapper /sbin/akmd
-
-For quicker turnaround when working on init itself, use:
-
-  mm -j &&
-  m ramdisk-nodeps &&
-  m bootimage-nodeps &&
-  adb reboot bootloader &&
-  fastboot boot $ANDROID_PRODUCT_OUT/boot.img
-
-Alternatively, use the emulator:
-
-  emulator -partition-size 1024 -verbose -show-kernel -no-window
diff --git a/init/service.cpp b/init/service.cpp
index a7eaf66..ba901fd 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -180,12 +180,6 @@
     }
 
     std::string prop_name = StringPrintf("init.svc.%s", name_.c_str());
-    if (prop_name.length() >= PROP_NAME_MAX) {
-        // If the property name would be too long, we can't set it.
-        LOG(ERROR) << "Property name \"init.svc." << name_ << "\" too long; not setting to " << new_state;
-        return;
-    }
-
     property_set(prop_name.c_str(), new_state.c_str());
 
     if (new_state == "running") {
@@ -312,13 +306,28 @@
 bool Service::ParseCapabilities(const std::vector<std::string>& args, std::string* err) {
     capabilities_ = 0;
 
+    if (!CapAmbientSupported()) {
+        *err = "capabilities requested but the kernel does not support ambient capabilities";
+        return false;
+    }
+
+    unsigned int last_valid_cap = GetLastValidCap();
+    if (last_valid_cap >= capabilities_.size()) {
+        LOG(WARNING) << "last valid run-time capability is larger than CAP_LAST_CAP";
+    }
+
     for (size_t i = 1; i < args.size(); i++) {
         const std::string& arg = args[i];
-        int cap = LookupCap(arg);
-        if (cap == -1) {
+        int res = LookupCap(arg);
+        if (res < 0) {
             *err = StringPrintf("invalid capability '%s'", arg.c_str());
             return false;
         }
+        unsigned int cap = static_cast<unsigned int>(res);  // |res| is >= 0.
+        if (cap > last_valid_cap) {
+            *err = StringPrintf("capability '%s' not supported by the kernel", arg.c_str());
+            return false;
+        }
         capabilities_[cap] = true;
     }
     return true;
@@ -567,12 +576,15 @@
             console_ = default_console;
         }
 
-        bool have_console = (open(console_.c_str(), O_RDWR | O_CLOEXEC) != -1);
-        if (!have_console) {
+        // Make sure that open call succeeds to ensure a console driver is
+        // properly registered for the device node
+        int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
+        if (console_fd < 0) {
             PLOG(ERROR) << "service '" << name_ << "' couldn't open console '" << console_ << "'";
             flags_ |= SVC_DISABLED;
             return false;
         }
+        close(console_fd);
     }
 
     struct stat sb;
@@ -979,7 +991,7 @@
     }
 
     if (svc->Reap()) {
-        waiting_for_exec = false;
+        stop_waiting_for_exec();
         RemoveService(*svc);
     }
 
@@ -1022,5 +1034,9 @@
 }
 
 bool ServiceParser::IsValidName(const std::string& name) const {
-    return is_legal_property_name("init.svc." + name);
+    // Property names can be any length, but may only contain certain characters.
+    // Property values can contain any characters, but may only be a certain length.
+    // (The latter restriction is needed because `start` and `stop` work by writing
+    // the service name to the "ctl.start" and "ctl.stop" properties.)
+    return is_legal_property_name("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
 }
diff --git a/init/test_service/Android.mk b/init/test_service/Android.mk
new file mode 100644
index 0000000..30c9e9d
--- /dev/null
+++ b/init/test_service/Android.mk
@@ -0,0 +1,27 @@
+# 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/init/test_service/README.md b/init/test_service/README.md
new file mode 100644
index 0000000..6773235
--- /dev/null
+++ b/init/test_service/README.md
@@ -0,0 +1,43 @@
+# Sample service for testing
+This is a sample service that can be used for testing init.
+
+## Design
+The service includes a `.rc` file that allows starting it from init.
+
+    service test_service /system/bin/test_service CapAmb 0000000000003000
+        class main
+        user system
+        group system
+        capabilities NET_ADMIN NET_RAW
+        disabled
+        oneshot
+
+The service accepts any even number of arguments on the command line
+(i.e. any number of pairs of arguments.)
+It will attempt to find the first element of each pair of arguments in
+`/proc/self/status`, and attempt to exactly match the second element of the pair
+to the relevant line of `proc/self/status`.
+
+### Example
+In the above case, the service will look for lines containing `CapAmb`:
+
+    cat /proc/self/status
+    ...
+    CapAmb:	0000000000003000
+
+And then attempt to exactly match the token after `:`, `0000000000003000`,
+with the command-line argument.
+If they match, the service exits successfully. If not, the service will exit
+with an error.
+
+## Usage
+	mmma -j <jobs> system/core/init/testservice
+	adb root
+	adb remount
+	adb sync
+	adb reboot
+	adb root
+	adb shell start test_service
+	adb logcat -b all -d | grep test_service
+
+Look for an exit status of 0.
diff --git a/init/test_service/test_service.cpp b/init/test_service/test_service.cpp
new file mode 100644
index 0000000..e7206f8
--- /dev/null
+++ b/init/test_service/test_service.cpp
@@ -0,0 +1,78 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <unistd.h>
+
+#include <map>
+#include <sstream>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+void Usage(char* argv[]) {
+    printf("Usage: %s <status field> <value> [<status field> <value>]*\n", argv[0]);
+    printf("E.g.: $ %s Uid \"1000 1000 1000 1000\"\n", argv[0]);
+}
+
+int main(int argc, char* argv[]) {
+    if (argc < 3) {
+        Usage(argv);
+        LOG(FATAL) << "no status field requested";
+    }
+    if (argc % 2 == 0) {
+        // Since |argc| counts argv[0], if |argc| is odd, then the number of
+        // command-line arguments is even.
+        Usage(argv);
+        LOG(FATAL) << "need even number of command-line arguments";
+    }
+
+    std::string status;
+    bool res = android::base::ReadFileToString("/proc/self/status", &status, true);
+    if (!res) {
+        PLOG(FATAL) << "could not read /proc/self/status";
+    }
+
+    std::map<std::string, std::string> fields;
+    std::vector<std::string> lines = android::base::Split(status, "\n");
+    for (const auto& line : lines) {
+        std::vector<std::string> tokens = android::base::Split(line, ":");
+        if (tokens.size() >= 2) {
+            std::string field = tokens[0];
+            std::string value = android::base::Trim(tokens[1]);
+            if (field.length() > 0) {
+                fields[field] = value;
+            }
+        }
+    }
+
+    bool test_fails = false;
+    size_t uargc = static_cast<size_t>(argc);  // |argc| >= 3.
+    for (size_t i = 1; i < static_cast<size_t>(argc); i = i + 2) {
+        std::string expected_value = argv[i + 1];
+        auto f = fields.find(argv[i]);
+        if (f != fields.end()) {
+            if (f->second != expected_value) {
+                LOG(ERROR) << "field '" << argv[i] << "' expected '" << expected_value
+                           << "', actual '" << f->second << "'";
+                test_fails = true;
+            }
+        } else {
+            LOG(WARNING) << "could not find field '" << argv[i] << "'";
+        }
+    }
+
+    return test_fails ? 1 : 0;
+}
diff --git a/init/test_service/test_service.rc b/init/test_service/test_service.rc
new file mode 100644
index 0000000..91e1a0f
--- /dev/null
+++ b/init/test_service/test_service.rc
@@ -0,0 +1,7 @@
+service test_service /system/bin/test_service CapAmb 0000000000003000
+    class main
+    user system
+    group system
+    capabilities NET_ADMIN NET_RAW
+    disabled
+    oneshot
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 361b925..915afbd 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -60,9 +60,18 @@
     cb.func_log = selinux_klog_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 
-    std::string hardware = property_get("ro.hardware");
-
     ueventd_parse_config_file("/ueventd.rc");
+    ueventd_parse_config_file("/vendor/ueventd.rc");
+    ueventd_parse_config_file("/odm/ueventd.rc");
+
+    /*
+     * keep the current product name base configuration so
+     * we remain backwards compatible and allow it to override
+     * everything
+     * TODO: cleanup platform ueventd.rc to remove vendor specific
+     * device node entries (b/34968103)
+     */
+    std::string hardware = property_get("ro.hardware");
     ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware.c_str()).c_str());
 
     device_init();
diff --git a/init/util.cpp b/init/util.cpp
index a79a419..888a366 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -185,18 +185,18 @@
     return okay;
 }
 
-int write_file(const char* path, const char* content) {
+bool write_file(const char* path, const char* content) {
     int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY|O_CREAT|O_NOFOLLOW|O_CLOEXEC, 0600));
     if (fd == -1) {
         PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
-        return -1;
+        return false;
     }
-    int result = android::base::WriteStringToFd(content, fd) ? 0 : -1;
-    if (result == -1) {
+    bool success = android::base::WriteStringToFd(content, fd);
+    if (!success) {
         PLOG(ERROR) << "write_file: Unable to write to '" << path << "'";
     }
     close(fd);
-    return result;
+    return success;
 }
 
 boot_clock::time_point boot_clock::now() {
diff --git a/init/util.h b/init/util.h
index e63c469..5c38dc3 100644
--- a/init/util.h
+++ b/init/util.h
@@ -33,7 +33,7 @@
                   uid_t uid, gid_t gid, const char *socketcon);
 
 bool read_file(const char* path, std::string* content);
-int write_file(const char* path, const char* content);
+bool write_file(const char* path, const char* content);
 
 // A std::chrono clock based on CLOCK_BOOTTIME.
 class boot_clock {
@@ -55,8 +55,8 @@
     return std::chrono::duration_cast<double_duration>(boot_clock::now() - start_).count();
   }
 
-  int64_t duration_ns() const {
-    return (boot_clock::now() - start_).count();
+  int64_t duration_ms() const {
+    return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_).count();
   }
 
  private:
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc
index 3ade31c..8fb2dbc 100644
--- a/libappfuse/FuseBuffer.cc
+++ b/libappfuse/FuseBuffer.cc
@@ -23,6 +23,7 @@
 #include <algorithm>
 #include <type_traits>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 
@@ -34,44 +35,65 @@
     "FuseBuffer must be standard layout union.");
 
 template <typename T>
-bool FuseMessage<T>::CheckHeaderLength() const {
+bool FuseMessage<T>::CheckHeaderLength(const char* name) const {
   const auto& header = static_cast<const T*>(this)->header;
-  if (sizeof(header) <= header.len && header.len <= sizeof(T)) {
+  if (header.len >= sizeof(header) && header.len <= sizeof(T)) {
     return true;
   } else {
-    LOG(ERROR) << "Packet size is invalid=" << header.len;
-    return false;
-  }
-}
-
-template <typename T>
-bool FuseMessage<T>::CheckResult(
-    int result, const char* operation_name) const {
-  const auto& header = static_cast<const T*>(this)->header;
-  if (result >= 0 && static_cast<uint32_t>(result) == header.len) {
-    return true;
-  } else {
-    PLOG(ERROR) << "Failed to " << operation_name
-        << " a packet. result=" << result << " header.len="
-        << header.len;
+    LOG(ERROR) << "Invalid header length is found in " << name << ": " <<
+        header.len;
     return false;
   }
 }
 
 template <typename T>
 bool FuseMessage<T>::Read(int fd) {
-  const ssize_t result = TEMP_FAILURE_RETRY(::read(fd, this, sizeof(T)));
-  return CheckHeaderLength() && CheckResult(result, "read");
+  char* const buf = reinterpret_cast<char*>(this);
+  const ssize_t result = TEMP_FAILURE_RETRY(::read(fd, buf, sizeof(T)));
+  if (result < 0) {
+    PLOG(ERROR) << "Failed to read a FUSE message";
+    return false;
+  }
+
+  const auto& header = static_cast<const T*>(this)->header;
+  if (result < static_cast<ssize_t>(sizeof(header))) {
+    LOG(ERROR) << "Read bytes " << result << " are shorter than header size " <<
+        sizeof(header);
+    return false;
+  }
+
+  if (!CheckHeaderLength("Read")) {
+    return false;
+  }
+
+  if (static_cast<uint32_t>(result) > header.len) {
+    LOG(ERROR) << "Read bytes " << result << " are longer than header.len " <<
+        header.len;
+    return false;
+  }
+
+  if (!base::ReadFully(fd, buf + result, header.len - result)) {
+    PLOG(ERROR) << "ReadFully failed";
+    return false;
+  }
+
+  return true;
 }
 
 template <typename T>
 bool FuseMessage<T>::Write(int fd) const {
-  const auto& header = static_cast<const T*>(this)->header;
-  if (!CheckHeaderLength()) {
+  if (!CheckHeaderLength("Write")) {
     return false;
   }
-  const ssize_t result = TEMP_FAILURE_RETRY(::write(fd, this, header.len));
-  return CheckResult(result, "write");
+
+  const char* const buf = reinterpret_cast<const char*>(this);
+  const auto& header = static_cast<const T*>(this)->header;
+  if (!base::WriteFully(fd, buf, header.len)) {
+    PLOG(ERROR) << "WriteFully failed";
+    return false;
+  }
+
+  return true;
 }
 
 template class FuseMessage<FuseRequest>;
diff --git a/libappfuse/include/libappfuse/FuseBuffer.h b/libappfuse/include/libappfuse/FuseBuffer.h
index e7f620c..7abd2fa 100644
--- a/libappfuse/include/libappfuse/FuseBuffer.h
+++ b/libappfuse/include/libappfuse/FuseBuffer.h
@@ -34,8 +34,7 @@
   bool Read(int fd);
   bool Write(int fd) const;
  private:
-  bool CheckHeaderLength() const;
-  bool CheckResult(int result, const char* operation_name) const;
+  bool CheckHeaderLength(const char* name) const;
 };
 
 // FuseRequest represents file operation requests from /dev/fuse. It starts
diff --git a/libappfuse/tests/FuseBufferTest.cc b/libappfuse/tests/FuseBufferTest.cc
index c822135..1a1abd5 100644
--- a/libappfuse/tests/FuseBufferTest.cc
+++ b/libappfuse/tests/FuseBufferTest.cc
@@ -20,6 +20,8 @@
 #include <string.h>
 #include <sys/socket.h>
 
+#include <thread>
+
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
 
@@ -29,7 +31,7 @@
 constexpr char kTempFile[] = "/data/local/tmp/appfuse_test_dump";
 
 void OpenTempFile(android::base::unique_fd* fd) {
-  fd->reset(open(kTempFile, O_CREAT | O_RDWR));
+  fd->reset(open(kTempFile, O_CREAT | O_RDWR, 0600));
   ASSERT_NE(-1, *fd) << strerror(errno);
   unlink(kTempFile);
   ASSERT_NE(-1, *fd) << strerror(errno);
@@ -110,6 +112,30 @@
   TestWriteInvalidLength(sizeof(fuse_in_header) - 1);
 }
 
+TEST(FuseMessageTest, ShortWriteAndRead) {
+  int raw_fds[2];
+  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, raw_fds));
+
+  android::base::unique_fd fds[2];
+  fds[0].reset(raw_fds[0]);
+  fds[1].reset(raw_fds[1]);
+
+  const int send_buffer_size = 1024;
+  ASSERT_EQ(0, setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &send_buffer_size,
+                          sizeof(int)));
+
+  bool succeed = false;
+  const int sender_fd = fds[0].get();
+  std::thread thread([sender_fd, &succeed] {
+    FuseRequest request;
+    request.header.len = 1024 * 4;
+    succeed = request.Write(sender_fd);
+  });
+  thread.detach();
+  FuseRequest request;
+  ASSERT_TRUE(request.Read(fds[1]));
+}
+
 TEST(FuseResponseTest, Reset) {
   FuseResponse response;
   // Write 1 to the first ten bytes.
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 200b6d6..5b31ecb 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -22,27 +22,13 @@
         "-Werror",
     ],
 
+    // The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
     clang_cflags: ["-Wno-inline-asm"],
 
-    // The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
     include_dirs: ["external/libunwind/include/tdep"],
 
-    // TODO: LLVM_DEVICE_BUILD_MK
-    // TODO: LLVM_HOST_BUILD_MK
 
     target: {
-        host: {
-            // -fno-omit-frame-pointer should be set for host build. Because currently
-            // libunwind can't recognize .debug_frame using dwarf version 4, and it relies
-            // on stack frame pointer to do unwinding on x86.
-            // $(LLVM_HOST_BUILD_MK) overwrites -fno-omit-frame-pointer. so the below line
-            // must be after the include.
-            cflags: [
-                "-Wno-extern-c-compat",
-                "-fno-omit-frame-pointer",
-            ],
-        },
-
         darwin: {
             enabled: false,
         },
@@ -130,4 +116,95 @@
             ],
         },
     }
-}
\ No newline at end of file
+}
+
+//-------------------------------------------------------------------------
+// The libbacktrace_offline static library.
+//-------------------------------------------------------------------------
+cc_library_static {
+    name: "libbacktrace_offline",
+    defaults: ["libbacktrace_common"],
+    host_supported: true,
+    srcs: ["BacktraceOffline.cpp"],
+
+    cflags: [
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_LIMIT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+
+    header_libs: ["llvm-headers"],
+
+    // Use shared libraries so their headers get included during build.
+    shared_libs = [
+        "libbase",
+        "libunwind",
+    ],
+}
+
+//-------------------------------------------------------------------------
+// The backtrace_test executable.
+//-------------------------------------------------------------------------
+cc_test {
+    name: "backtrace_test",
+    defaults: ["libbacktrace_common"],
+    host_supported: true,
+    srcs: [
+        "backtrace_offline_test.cpp",
+        "backtrace_test.cpp",
+        "GetPss.cpp",
+        "thread_utils.c",
+    ],
+
+    cflags: [
+        "-fno-builtin",
+        "-O0",
+        "-g",
+    ],
+
+    shared_libs: [
+        "libbacktrace_test",
+        "libbacktrace",
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libunwind",
+    ],
+
+    group_static_libs: true,
+
+    // Statically link LLVMlibraries to remove dependency on llvm shared library.
+    static_libs = [
+        "libbacktrace_offline",
+        "libLLVMObject",
+        "libLLVMBitReader",
+        "libLLVMMC",
+        "libLLVMMCParser",
+        "libLLVMCore",
+        "libLLVMSupport",
+
+        "libziparchive",
+        "libz",
+    ],
+
+    header_libs: ["llvm-headers"],
+
+    target: {
+        android: {
+            cflags: ["-DENABLE_PSS_TESTS"],
+            shared_libs: [
+                "libdl",
+                "libutils",
+            ],
+        },
+        linux: {
+            host_ldlibs: [
+                "-lpthread",
+                "-lrt",
+                "-ldl",
+                "-lncurses",
+            ],
+            static_libs: ["libutils"],
+        },
+    },
+}
diff --git a/libbacktrace/Android.build.mk b/libbacktrace/Android.build.mk
deleted file mode 100644
index 2467f3e..0000000
--- a/libbacktrace/Android.build.mk
+++ /dev/null
@@ -1,95 +0,0 @@
-#
-# 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.
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := $(module)
-LOCAL_MODULE_TAGS := $(module_tag)
-LOCAL_MULTILIB := $($(module)_multilib)
-ifeq ($(LOCAL_MULTILIB),both)
-ifneq ($(build_target),$(filter $(build_target),SHARED_LIBRARY STATIC_LIBRARY))
-  LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-  LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-endif
-endif
-
-ifeq ($(build_type),target)
-  include $(LLVM_DEVICE_BUILD_MK)
-else
-  include $(LLVM_HOST_BUILD_MK)
-endif
-
-LOCAL_ADDITIONAL_DEPENDENCIES += \
-    $(LOCAL_PATH)/Android.mk \
-    $(LOCAL_PATH)/Android.build.mk \
-
-LOCAL_CFLAGS += \
-    $(libbacktrace_common_cflags) \
-    $($(module)_cflags) \
-    $($(module)_cflags_$(build_type)) \
-
-LOCAL_CLANG_CFLAGS += \
-    $(libbacktrace_common_clang_cflags) \
-
-LOCAL_CONLYFLAGS += \
-    $(libbacktrace_common_conlyflags) \
-    $($(module)_conlyflags) \
-    $($(module)_conlyflags_$(build_type)) \
-
-LOCAL_CPPFLAGS += \
-    $(libbacktrace_common_cppflags) \
-    $($(module)_cppflags) \
-    $($(module)_cppflags_$(build_type)) \
-
-LOCAL_C_INCLUDES += \
-    $(libbacktrace_common_c_includes) \
-    $($(module)_c_includes) \
-    $($(module)_c_includes_$(build_type)) \
-
-LOCAL_SRC_FILES := \
-    $($(module)_src_files) \
-    $($(module)_src_files_$(build_type)) \
-
-LOCAL_STATIC_LIBRARIES += \
-    $($(module)_static_libraries) \
-    $($(module)_static_libraries_$(build_type)) \
-
-LOCAL_SHARED_LIBRARIES += \
-    $($(module)_shared_libraries) \
-    $($(module)_shared_libraries_$(build_type)) \
-
-LOCAL_LDLIBS += \
-    $($(module)_ldlibs) \
-    $($(module)_ldlibs_$(build_type)) \
-
-LOCAL_STRIP_MODULE := $($(module)_strip_module)
-
-ifeq ($(build_type),target)
-  include $(BUILD_$(build_target))
-endif
-
-ifeq ($(build_type),host)
-  # Only build if host builds are supported.
-  ifeq ($(build_host),true)
-    # -fno-omit-frame-pointer should be set for host build. Because currently
-    # libunwind can't recognize .debug_frame using dwarf version 4, and it relies
-    # on stack frame pointer to do unwinding on x86.
-    # $(LLVM_HOST_BUILD_MK) overwrites -fno-omit-frame-pointer. so the below line
-    # must be after the include.
-    LOCAL_CFLAGS += -Wno-extern-c-compat -fno-omit-frame-pointer
-    include $(BUILD_HOST_$(build_target))
-  endif
-endif
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
deleted file mode 100644
index f4976e9..0000000
--- a/libbacktrace/Android.mk
+++ /dev/null
@@ -1,121 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-libbacktrace_common_cflags := \
-	-Wall \
-	-Werror \
-
-libbacktrace_common_c_includes := \
-	external/libunwind/include/tdep \
-
-# The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
-libbacktrace_common_clang_cflags += \
-    -Wno-inline-asm
-
-build_host := false
-ifeq ($(HOST_OS),linux)
-ifeq ($(HOST_ARCH),$(filter $(HOST_ARCH),x86 x86_64))
-build_host := true
-endif
-endif
-
-LLVM_ROOT_PATH := external/llvm
-include $(LLVM_ROOT_PATH)/llvm.mk
-
-#-------------------------------------------------------------------------
-# The libbacktrace_offline static library.
-#-------------------------------------------------------------------------
-libbacktrace_offline_src_files := \
-	BacktraceOffline.cpp \
-
-# Use shared libraries so their headers get included during build.
-libbacktrace_offline_shared_libraries := \
-	libbase \
-	libunwind \
-
-module := libbacktrace_offline
-build_type := target
-build_target := STATIC_LIBRARY
-libbacktrace_offline_multilib := both
-include $(LOCAL_PATH)/Android.build.mk
-build_type := host
-include $(LOCAL_PATH)/Android.build.mk
-
-#-------------------------------------------------------------------------
-# The backtrace_test executable.
-#-------------------------------------------------------------------------
-backtrace_test_cflags := \
-	-fno-builtin \
-	-O0 \
-	-g \
-
-backtrace_test_cflags_target := \
-	-DENABLE_PSS_TESTS \
-
-backtrace_test_src_files := \
-	backtrace_offline_test.cpp \
-	backtrace_test.cpp \
-	GetPss.cpp \
-	thread_utils.c \
-
-backtrace_test_ldlibs_host := \
-	-lpthread \
-	-lrt \
-
-backtrace_test_shared_libraries := \
-	libbacktrace_test \
-	libbacktrace \
-	libbase \
-	libcutils \
-	liblog \
-	libunwind \
-
-backtrace_test_shared_libraries_target += \
-	libdl \
-	libutils \
-
-# Statically link LLVMlibraries to remove dependency on llvm shared library.
-backtrace_test_static_libraries := \
-	libbacktrace_offline \
-	libLLVMObject \
-	libLLVMBitReader \
-	libLLVMMC \
-	libLLVMMCParser \
-	libLLVMCore \
-	libLLVMSupport \
-
-backtrace_test_static_libraries_target := \
-	libziparchive \
-	libz \
-
-backtrace_test_static_libraries_host := \
-	libziparchive \
-	libz \
-	libutils \
-
-backtrace_test_ldlibs_host += \
-	-ldl \
-
-module := backtrace_test
-module_tag := debug
-build_type := target
-build_target := NATIVE_TEST
-backtrace_test_multilib := both
-include $(LOCAL_PATH)/Android.build.mk
-build_type := host
-include $(LOCAL_PATH)/Android.build.mk
diff --git a/libbacktrace/BacktraceLog.h b/libbacktrace/BacktraceLog.h
index 0a27982..5c39f1c 100644
--- a/libbacktrace/BacktraceLog.h
+++ b/libbacktrace/BacktraceLog.h
@@ -19,7 +19,7 @@
 
 #define LOG_TAG "libbacktrace"
 
-#include <android/log.h>
+#include <log/log.h>
 
 // Macro to log the function name along with the warning message.
 #define BACK_LOGW(format, ...) \
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index 4496375..0e31495 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -22,7 +22,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <android/log.h>
+#include <log/log.h>
+
 #include <backtrace/backtrace_constants.h>
 #include <backtrace/BacktraceMap.h>
 
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
index 9e95563..0a2f5a3 100644
--- a/libbacktrace/BacktraceOffline.cpp
+++ b/libbacktrace/BacktraceOffline.cpp
@@ -21,6 +21,7 @@
 #include <dwarf.h>
 }
 
+#include <pthread.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
@@ -30,10 +31,12 @@
 #include <unistd.h>
 
 #include <memory>
+#include <mutex>
 #include <string>
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/macros.h>
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 #include <ziparchive/zip_archive.h>
@@ -89,9 +92,6 @@
       has_debug_frame(false), has_gnu_debugdata(false) { }
 };
 
-static std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>>& g_debug_frames =
-    *new std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>>;
-
 void Space::Clear() {
   start = 0;
   end = 0;
@@ -281,39 +281,14 @@
 
   // vaddr in the elf file.
   uint64_t ip_vaddr = ip - map.start + debug_frame->min_vaddr;
-  if (debug_frame->has_arm_exidx) {
-    auto& func_vaddrs = debug_frame->arm_exidx.func_vaddr_array;
-    if (ip_vaddr >= func_vaddrs[0] && ip_vaddr < debug_frame->text_end_vaddr) {
-      // Use binary search to find the correct function.
-      auto it = std::upper_bound(func_vaddrs.begin(), func_vaddrs.end(),
-                                 static_cast<uint32_t>(ip_vaddr));
-      if (it != func_vaddrs.begin()) {
-        --it;
-        // Found the exidx entry.
-        size_t index = it - func_vaddrs.begin();
 
-        proc_info->format = UNW_INFO_FORMAT_ARM_EXIDX;
-        proc_info->unwind_info = reinterpret_cast<void*>(
-            static_cast<uintptr_t>(index * sizeof(ArmIdxEntry) +
-                                   debug_frame->arm_exidx.exidx_vaddr +
-                                   debug_frame->min_vaddr));
-
-        // Prepare arm_exidx space and arm_extab space.
-        arm_exidx_space_.start = debug_frame->min_vaddr + debug_frame->arm_exidx.exidx_vaddr;
-        arm_exidx_space_.end = arm_exidx_space_.start +
-            debug_frame->arm_exidx.exidx_data.size() * sizeof(ArmIdxEntry);
-        arm_exidx_space_.data = reinterpret_cast<const uint8_t*>(
-            debug_frame->arm_exidx.exidx_data.data());
-
-        arm_extab_space_.start = debug_frame->min_vaddr + debug_frame->arm_exidx.extab_vaddr;
-        arm_extab_space_.end = arm_extab_space_.start +
-            debug_frame->arm_exidx.extab_data.size();
-        arm_extab_space_.data = debug_frame->arm_exidx.extab_data.data();
-        return true;
-      }
-    }
-  }
-
+  // The unwind info can come from .ARM.exidx or .eh_frame, or .debug_frame/.gnu_debugdata.
+  // First check .eh_frame/.debug_frame, then check .ARM.exidx. Because .eh_frame/.debug_frame has
+  // function range for each entry, by matching ip address with the function range, we know exactly
+  // whether the ip address hits an entry. But .ARM.exidx doesn't have function range for each
+  // entry, it thinks that an ip address hits an entry when (entry.addr <= ip < next_entry.addr).
+  // To prevent ip addresses hit in .eh_frame/.debug_frame being regarded as addresses hit in
+  // .ARM.exidx, we need to check .eh_frame/.debug_frame first.
   if (debug_frame->has_eh_frame) {
     if (ip_vaddr >= debug_frame->eh_frame.min_func_vaddr &&
         ip_vaddr < debug_frame->text_end_vaddr) {
@@ -322,7 +297,6 @@
       eh_frame_hdr_space_.end =
           eh_frame_hdr_space_.start + debug_frame->eh_frame.hdr_data.size();
       eh_frame_hdr_space_.data = debug_frame->eh_frame.hdr_data.data();
-
       eh_frame_space_.start = ip - ip_vaddr + debug_frame->eh_frame.vaddr;
       eh_frame_space_.end = eh_frame_space_.start + debug_frame->eh_frame.data.size();
       eh_frame_space_.data = debug_frame->eh_frame.data.data();
@@ -344,7 +318,6 @@
       }
     }
   }
-
   if (debug_frame->has_debug_frame || debug_frame->has_gnu_debugdata) {
     unw_dyn_info_t di;
     unw_word_t segbase = map.start - map.offset;
@@ -358,6 +331,40 @@
       }
     }
   }
+
+  if (debug_frame->has_arm_exidx) {
+    auto& func_vaddrs = debug_frame->arm_exidx.func_vaddr_array;
+    if (ip_vaddr >= func_vaddrs[0] && ip_vaddr < debug_frame->text_end_vaddr) {
+      // Use binary search to find the correct function.
+      auto it = std::upper_bound(func_vaddrs.begin(), func_vaddrs.end(),
+                                 static_cast<uint32_t>(ip_vaddr));
+      if (it != func_vaddrs.begin()) {
+        --it;
+        // Found the exidx entry.
+        size_t index = it - func_vaddrs.begin();
+        proc_info->start_ip = *it;
+        proc_info->format = UNW_INFO_FORMAT_ARM_EXIDX;
+        proc_info->unwind_info = reinterpret_cast<void*>(
+            static_cast<uintptr_t>(index * sizeof(ArmIdxEntry) +
+                                   debug_frame->arm_exidx.exidx_vaddr +
+                                   debug_frame->min_vaddr));
+        eh_frame_hdr_space_.Clear();
+        eh_frame_space_.Clear();
+        // Prepare arm_exidx space and arm_extab space.
+        arm_exidx_space_.start = debug_frame->min_vaddr + debug_frame->arm_exidx.exidx_vaddr;
+        arm_exidx_space_.end = arm_exidx_space_.start +
+            debug_frame->arm_exidx.exidx_data.size() * sizeof(ArmIdxEntry);
+        arm_exidx_space_.data = reinterpret_cast<const uint8_t*>(
+            debug_frame->arm_exidx.exidx_data.data());
+
+        arm_extab_space_.start = debug_frame->min_vaddr + debug_frame->arm_exidx.extab_vaddr;
+        arm_extab_space_.end = arm_extab_space_.start +
+            debug_frame->arm_exidx.extab_data.size();
+        arm_extab_space_.data = debug_frame->arm_exidx.extab_data.data();
+        return true;
+      }
+    }
+  }
   return false;
 }
 
@@ -534,6 +541,10 @@
     default:
       result = false;
   }
+#else
+  UNUSED(reg);
+  UNUSED(value);
+  result = false;
 #endif
   return result;
 }
@@ -544,18 +555,31 @@
   return "";
 }
 
+static std::mutex g_lock;
+static std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>>* g_debug_frames = nullptr;
+
 static DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename);
 
 DebugFrameInfo* BacktraceOffline::GetDebugFrameInFile(const std::string& filename) {
   if (cache_file_) {
-    auto it = g_debug_frames.find(filename);
-    if (it != g_debug_frames.end()) {
-      return it->second.get();
+    std::lock_guard<std::mutex> lock(g_lock);
+    if (g_debug_frames != nullptr) {
+      auto it = g_debug_frames->find(filename);
+      if (it != g_debug_frames->end()) {
+        return it->second.get();
+      }
     }
   }
   DebugFrameInfo* debug_frame = ReadDebugFrameFromFile(filename);
   if (cache_file_) {
-      g_debug_frames.emplace(filename, std::unique_ptr<DebugFrameInfo>(debug_frame));
+    std::lock_guard<std::mutex> lock(g_lock);
+    if (g_debug_frames == nullptr) {
+      g_debug_frames = new std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>>;
+    }
+    auto pair = g_debug_frames->emplace(filename, std::unique_ptr<DebugFrameInfo>(debug_frame));
+    if (!pair.second) {
+      debug_frame = pair.first->second.get();
+    }
   }
   return debug_frame;
 }
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
index 49fcb29..465b3f9 100644
--- a/libbacktrace/backtrace_offline_test.cpp
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -220,25 +220,7 @@
 #endif
 }
 
-static void BacktraceOfflineTest(const std::string& testlib_name) {
-  const std::string arch = GetArch();
-  if (arch.empty()) {
-    GTEST_LOG_(INFO) << "This test does nothing on current arch.";
-    return;
-  }
-  const std::string offline_testdata_path = "testdata/" + arch + "/offline_testdata";
-  std::string testdata;
-  ASSERT_TRUE(android::base::ReadFileToString(offline_testdata_path, &testdata));
-
-  const std::string testlib_path = "testdata/" + arch + "/" + testlib_name;
-  struct stat st;
-  if (stat(testlib_path.c_str(), &st) == -1) {
-    GTEST_LOG_(INFO) << "This test is skipped as " << testlib_path << " doesn't exist.";
-    return;
-  }
-
-  // Parse offline_testdata.
-  std::vector<std::string> lines = android::base::Split(testdata, "\n");
+struct OfflineTestData {
   int pid;
   int tid;
   std::vector<backtrace_map_t> maps;
@@ -246,63 +228,93 @@
   backtrace_stackinfo_t stack_info;
   std::vector<uint8_t> stack;
   std::vector<FunctionSymbol> symbols;
+};
+
+bool ReadOfflineTestData(const std::string offline_testdata_path, OfflineTestData* testdata) {
+  std::string s;
+  if (!android::base::ReadFileToString(offline_testdata_path, &s)) {
+    return false;
+  }
+  // Parse offline_testdata.
+  std::vector<std::string> lines = android::base::Split(s, "\n");
+  memset(&testdata->unw_context, 0, sizeof(testdata->unw_context));
   for (const auto& line : lines) {
     if (android::base::StartsWith(line, "pid:")) {
-      sscanf(line.c_str(), "pid: %d tid: %d", &pid, &tid);
+      sscanf(line.c_str(), "pid: %d tid: %d", &testdata->pid, &testdata->tid);
     } else if (android::base::StartsWith(line, "map:")) {
-      maps.resize(maps.size() + 1);
+      testdata->maps.resize(testdata->maps.size() + 1);
+      backtrace_map_t& map = testdata->maps.back();
       int pos;
       sscanf(line.c_str(),
              "map: start: %" SCNxPTR " end: %" SCNxPTR " offset: %" SCNxPTR
              " load_base: %" SCNxPTR " flags: %d name: %n",
-             &maps.back().start, &maps.back().end, &maps.back().offset,
-             &maps.back().load_base, &maps.back().flags, &pos);
-      maps.back().name = android::base::Trim(line.substr(pos));
+             &map.start, &map.end, &map.offset, &map.load_base, &map.flags, &pos);
+      map.name = android::base::Trim(line.substr(pos));
     } else if (android::base::StartsWith(line, "registers:")) {
       size_t size;
       int pos;
       sscanf(line.c_str(), "registers: %zu %n", &size, &pos);
-      ASSERT_EQ(sizeof(unw_context), size);
-      HexStringToRawData(&line[pos], &unw_context, size);
+      if (sizeof(testdata->unw_context) != size) {
+        return false;
+      }
+      HexStringToRawData(&line[pos], &testdata->unw_context, size);
     } else if (android::base::StartsWith(line, "stack:")) {
       size_t size;
       int pos;
       sscanf(line.c_str(),
              "stack: start: %" SCNx64 " end: %" SCNx64 " size: %zu %n",
-             &stack_info.start, &stack_info.end, &size, &pos);
-      stack.resize(size);
-      HexStringToRawData(&line[pos], &stack[0], size);
-      stack_info.data = stack.data();
+             &testdata->stack_info.start, &testdata->stack_info.end, &size, &pos);
+      testdata->stack.resize(size);
+      HexStringToRawData(&line[pos], &testdata->stack[0], size);
+      testdata->stack_info.data = testdata->stack.data();
     } else if (android::base::StartsWith(line, "function:")) {
-      symbols.resize(symbols.size() + 1);
+      testdata->symbols.resize(testdata->symbols.size() + 1);
+      FunctionSymbol& symbol = testdata->symbols.back();
       int pos;
       sscanf(line.c_str(),
              "function: start: %" SCNxPTR " end: %" SCNxPTR " name: %n",
-             &symbols.back().start, &symbols.back().end,
-             &pos);
-      symbols.back().name = line.substr(pos);
+             &symbol.start, &symbol.end, &pos);
+      symbol.name = line.substr(pos);
     }
   }
+  return true;
+}
+
+static void BacktraceOfflineTest(const std::string& testlib_name) {
+  const std::string arch = GetArch();
+  if (arch.empty()) {
+    GTEST_LOG_(INFO) << "This test does nothing on current arch.";
+    return;
+  }
+  const std::string testlib_path = "testdata/" + arch + "/" + testlib_name;
+  struct stat st;
+  if (stat(testlib_path.c_str(), &st) == -1) {
+    GTEST_LOG_(INFO) << "This test is skipped as " << testlib_path << " doesn't exist.";
+    return;
+  }
+
+  const std::string offline_testdata_path = "testdata/" + arch + "/offline_testdata";
+  OfflineTestData testdata;
+  ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
 
   // Fix path of libbacktrace_testlib.so.
-  for (auto& map : maps) {
+  for (auto& map : testdata.maps) {
     if (map.name.find("libbacktrace_test.so") != std::string::npos) {
       map.name = testlib_path;
     }
   }
 
   // Do offline backtrace.
-  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid, maps));
+  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(testdata.pid, testdata.maps));
   ASSERT_TRUE(map != nullptr);
 
   std::unique_ptr<Backtrace> backtrace(
-      Backtrace::CreateOffline(pid, tid, map.get(), stack_info));
+      Backtrace::CreateOffline(testdata.pid, testdata.tid, map.get(), testdata.stack_info));
   ASSERT_TRUE(backtrace != nullptr);
 
-  ucontext_t ucontext = GetUContextFromUnwContext(unw_context);
+  ucontext_t ucontext = GetUContextFromUnwContext(testdata.unw_context);
   ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
 
-
   // Collect pc values of the call stack frames.
   std::vector<uintptr_t> pc_values;
   for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
@@ -311,17 +323,20 @@
 
   size_t test_one_index = 0;
   for (size_t i = 0; i < pc_values.size(); ++i) {
-    if (FunctionNameForAddress(pc_values[i], symbols) == "test_level_one") {
+    if (FunctionNameForAddress(pc_values[i], testdata.symbols) == "test_level_one") {
       test_one_index = i;
       break;
     }
   }
 
   ASSERT_GE(test_one_index, 3u);
-  ASSERT_EQ("test_level_one", FunctionNameForAddress(pc_values[test_one_index], symbols));
-  ASSERT_EQ("test_level_two", FunctionNameForAddress(pc_values[test_one_index - 1], symbols));
-  ASSERT_EQ("test_level_three", FunctionNameForAddress(pc_values[test_one_index - 2], symbols));
-  ASSERT_EQ("test_level_four", FunctionNameForAddress(pc_values[test_one_index - 3], symbols));
+  ASSERT_EQ("test_level_one", FunctionNameForAddress(pc_values[test_one_index], testdata.symbols));
+  ASSERT_EQ("test_level_two", FunctionNameForAddress(pc_values[test_one_index - 1],
+                                                     testdata.symbols));
+  ASSERT_EQ("test_level_three", FunctionNameForAddress(pc_values[test_one_index - 2],
+                                                       testdata.symbols));
+  ASSERT_EQ("test_level_four", FunctionNameForAddress(pc_values[test_one_index - 3],
+                                                      testdata.symbols));
 }
 
 TEST(libbacktrace, offline_eh_frame) {
@@ -339,3 +354,47 @@
 TEST(libbacktrace, offline_arm_exidx) {
   BacktraceOfflineTest("libbacktrace_test_arm_exidx.so");
 }
+
+// This test tests the situation that ranges of functions covered by .eh_frame and .ARM.exidx
+// overlap with each other, which appears in /system/lib/libart.so.
+TEST(libbacktrace, offline_unwind_mix_eh_frame_and_arm_exidx) {
+  const std::string arch = GetArch();
+  if (arch.empty() || arch != "arm") {
+    GTEST_LOG_(INFO) << "This test does nothing on current arch.";
+    return;
+  }
+  const std::string testlib_path = "testdata/" + arch + "/libart.so";
+  struct stat st;
+  ASSERT_EQ(0, stat(testlib_path.c_str(), &st)) << "can't find testlib " << testlib_path;
+
+  const std::string offline_testdata_path = "testdata/" + arch + "/offline_testdata_for_libart";
+  OfflineTestData testdata;
+  ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
+
+  // Fix path of /system/lib/libart.so.
+  for (auto& map : testdata.maps) {
+    if (map.name.find("libart.so") != std::string::npos) {
+      map.name = testlib_path;
+    }
+  }
+
+  // Do offline backtrace.
+  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(testdata.pid, testdata.maps));
+  ASSERT_TRUE(map != nullptr);
+
+  std::unique_ptr<Backtrace> backtrace(
+      Backtrace::CreateOffline(testdata.pid, testdata.tid, map.get(), testdata.stack_info));
+  ASSERT_TRUE(backtrace != nullptr);
+
+  ucontext_t ucontext = GetUContextFromUnwContext(testdata.unw_context);
+  ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
+
+  // The last frame is outside of libart.so
+  ASSERT_EQ(testdata.symbols.size() + 1, backtrace->NumFrames());
+  for (size_t i = 0; i + 1 < backtrace->NumFrames(); ++i) {
+    uintptr_t vaddr_in_file = backtrace->GetFrame(i)->pc - testdata.maps[0].start +
+        testdata.maps[0].load_base;
+    std::string name = FunctionNameForAddress(vaddr_in_file, testdata.symbols);
+    ASSERT_EQ(name, testdata.symbols[i].name);
+  }
+}
diff --git a/libbacktrace/testdata/arm/libart.so b/libbacktrace/testdata/arm/libart.so
new file mode 100644
index 0000000..bed8e35
--- /dev/null
+++ b/libbacktrace/testdata/arm/libart.so
Binary files differ
diff --git a/libbacktrace/testdata/arm/offline_testdata_for_libart b/libbacktrace/testdata/arm/offline_testdata_for_libart
new file mode 100644
index 0000000..63f6a07
--- /dev/null
+++ b/libbacktrace/testdata/arm/offline_testdata_for_libart
@@ -0,0 +1,10 @@
+pid: 32232 tid: 32233
+registers: 64 000000000000000000000000000000006473602451b3e2e700000000d82fd1ff5600000000908eec00000000d42dd1ff00000000c02dd1ff617171e9617171e9
+map: start: e9380000 end: e9766000 offset: 0 load_base: b000 flags: 5 name: /system/lib/libart.so
+stack: start: ffd12dc0 end: ffd16000 size: 12864 00000000000c5024070000000300000005070a0a0100000051b3e2e700000000d82fd1ff560000004c2ed1ff000000000000000081b771e9d82fd1ff000000004c2ed1ff0c2ed1ff40a8d27024bf76e900908eec000000000834d1ff0000000000000000000000000d000000050000000000000000000000080000000101d1ff44b8bfeb4b0000000000000000000000e8b8952400000000fc2ed1ff4fb3e2e7bc49ac6f00908eecb02ed1ffd82fd1ff040000008c908eec942fd1ffd5c141e9d82fd1ff4fb3e2e7542fd1ff336c68e940000000400000007030d1fff031d1ff00000000bc49ac6f5c30d1ff942fd1ff842fd1ffd82fd1ff00000000b8f1786f4fb3e2e7610d67e9d82fd1ff4fb3e2e77880adeb7980adeb7a80adeb7b80adeb7c80adeb7d80adeb7e80adeb7f80adeb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007430d1ff02000000e8b89524e8d895240200000000908eec5c30d1ffbc49ac6f4fb3e2e74030d1ffe8d8952400000000b8f1786fbc49ac6f332367e94fb3e2e701000000637171e9637171e9000000005c30d1ff8430d1ffe0c08bec882fd1ff4fb3e2e70200000004000000942fd1ffe8b8952400908eec58d8952458d895247fbd69e90500000000400fe40100000000908eec58d89524060000009c86bd6f6b876fe900908eece0c08bec00008eec0000000000000000000000000000000044b8bfeb4b000000009be86f040000000038d1ff01000000c8e7446f060000000000000000908eec30d89524e8b895249c86bd6f7893476f00908eec00000000358c6fe970400fe4116e71e9a0285a6fa4d49c6f4489bd6f30d8952458d89524e8d8952400908eeca431d1ff2c31d1ffb75861e90100000000908eec30528bec409181e958d8952431abed6fac33576fb438d1ff030000007800502400000000a0005024060000007893476f00908eec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004489bd6f78005024d00c5024a0005024a431d1ff2c31d1ff9b99aa71a4d49c6f30d8952400000000e8d895244489bd6fa8e5bc6fc8b895240100000000000000b033d1ff56000000637171e900000000d00c5024c8b89524000000000100000000000000b033d1ff560000006431d1ffa431d1ff000000009fb671e9b033d1ff00000000a431d1ff6431d1ffc431d1ff000000000000000081b771e9b033d1ff00000000c431d1ff8431d1ff01000000020000002429000001000000dc501b002033d1ff0100000018f9736f0100000000908eec58d8952440f180e9a8ec01245b215ce8a4d49c6f00908eec0832d1ffb033d1ff040000008c908eeca832d1ffabc141e9b033d1ff5b215ce82832d1ffb033d1ff080000008c908eec000000000035d1ff0834d1ffa832d1ffa4d49c6f04000000cca312e800908eec6832d1ffb033d1ff0834d1ff6bc354e9b033d1ff5b215ce8cca312e800908eec8832d1ffb033d1ff0834d1ff6bc354e900908eeca4d49c6f44b8bfeb1833d1ff000000006832d1ffb033d1ff478054e9b033d1ff1b8054e90834d1ffa4d49c6f0000000000000000000000000000000008000000000000000834d1ff0000000000000000000000000000000000000000000000000000000058d895240000000000000000000000000000000000000000000000000000000058d89524b17e54e98c56af6f00000000a4d49c6f288944e800908eec00000000d032d1ff0000000000000000000000000000000000000000000000007e8ea6c358a58cec00f580e90834d1ffa4d49c6f58d8952400908eecb033d1ffe9100000da8844e8833c70e9e9100000b033d1ff0200000058d8952408b1796f0200000000908eecda8844e82c34d1ff00908eece9100000005d70e9070000007d1300006034d1ff98d170e9b033d1ff0834d1ff148844e800908eecb033d1ffa034d1ffa833d1ff0100000044b8bfeb41f252e9e91fdeeaa491deea000000004700000001000000d9c4ddea0000000000000000b834d1ff00b051ff0834d1ff00908eecf833d1ffa034d1ff148844e800000000020000004d4c53e900000000000000000000000000908eec44b8bfeb0834d1ff3835d1ff148844e85035d1ffbb936fe90000000044b8bfebb033d1ffda8844e8148844e8000000000d0000005a0000007d137d13d00c502400000000600480240400000070048024f80c5024170000000100000002000000000000000040000000000000d0018024d00c502400000000600480240000000070048024f80c5024000000000000000000000000000000000000000000000000d001802465906fe97b2e5ce8000000000300000000000000881388131a00000001000000000000004cdd76e9010000007b2e5ce8020000009835d1ff5835d1ffc435d1ff010000000000000000000000010000000000000000dd76e90834d1ff0d0000000100000000000000000000005035d1ff9036d1ff00000000a435d1ff7e8ea6c3080000000000000000000000000000000000000038cb7e7044b8bfeb7d2e5ce800000000c037d1ff5600000000908eec00000000cc35d1ff55af71e9e0285a6f040000000800000001000000a437d1ff010000001c73d870000000000000000043000000339768e9040000006c36d1ff0e000000b436d1ff8cc97e706c36d1ff0e00000018eb01243173d870040000007d2e5ce800000000c037d1ff5600000000000000cc35d1ff637171e90000000018eb012402000000010000007d2e5ce800000000c037d1ff560000004436d1ff000000000000000081b771e9c037d1ff000000004436d1ff0436d1ff00e68dec0800000001000000a437d1ff010000001c73d870000000000000000043000000339768e9040000006c36d1ff0e000000b436d1ff8cc97e706c36d1ff0e000000adf861e918eb01243173d870040000007b2e5ce844b8bfeb00908eeca836d1ffc037d1ff040000008c908eec7c37d1ffd5c141e9c037d1ff7b2e5ce80000000000908eecd036d1ff00000000b038d1ff183ad1ff0000000044b8bfeb1038d1ff7c37d1ff6c37d1ffc037d1ff7b2e5ce8000000007b2e5ce8610d67e9c037d1ff7b2e5ce8280477e99835456f960300009a35456f10aa5e6f9a35456f9835456f68b85e6f881e77e9b30a47e9e81382e94c95b4ec7100000000908eec9c908eec30528bec1038d1ff7b2e5ce800000000c78469e91038d1ff0aeb3b52208989ec150645e9010000001038d1ff6c37d1ff44b8bfeb6c37d1ff00000000d837d1ff1038d1ff7b2e5ce8000000006c38d1ff8f0b67e97b2e5ce818eb012400000000000000000838d1ff7b2e5ce802000000040000007c37d1ff18eb01249835456f00000000901e77e9180000000300000000908eec480000004800000043000000640477e97669747954687265070000001a00000060eb0124000000000000000000000000a500000044b8bfeb1038d1ff00908eeceeac73e943000000640477e9901e77e9e6ac73e961705ce96c38d1ff18eb012400908eeceeac73e943000000640477e9000059008bc95ce900908eec30528bec409181e900908eec430000005900000000528bec409181e900004300710000000300000030528bec89c75ce944b8bfebe2050000103dd1ff03000000a3f6a7eb89c75ce96c38d1ff7e8ea6c389c75ce997f5a7eb710000000000000030528bec7e8ea6c3e83cd1ff2079002488beba6ff0ca726f5600000000908eec000000005439d1ff8b1db8aa803a89ec7e8ea6c3000000009173d870ec55af6f00000000010000004892796f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003801802488beba6ff0ca726f56000000000000005439d1ff9d3daa71cc55af6f7039d1ff0b0000006839d1ff7d2e5ce800000000483bd1ff637171e900000000207900240b00000058a58cec40f180e9010000007d2e5ce800000000483bd1ff56000000cc39d1ff000000000000000081b771e9483bd1ff00000000cc39d1ff8c39d1ff05000000050000000c000000040000000100000012000000958c00000100000074d73500483bd1ff01000000e880746f0100000000908eec903ad1ff40f180e97e02000000908eec2079002400000000383ad1ff7b2e5ce8cc55af6f00908eec303ad1ff483bd1ff040000008c908eec043bd1ffd5c141e9483bd1ff7b2e5ce840f180e900908eec583ad1ff00000000000000000000000000000000cc55af6f983bd1ff043bd1fff43ad1ff483bd1ff7b2e5ce8000000007b2e5ce8610d67e9483bd1ff7b2e5ce8280477e94892796f860100004e92796f00a088ec4e92796f4892796f18a688ec881e77e9b30a47e978e388ec4c95b4ec2100000000908eec9c908eec30528bec983bd1ff7b2e5ce800000000c78469e9983bd1ff06b005fdf0298aec150645e901000000983bd1fff43ad1ffcc55af6ff43ad1ff00000000603bd1ff983bd1ff7b2e5ce800000000f43bd1ff8f0b67e97b2e5ce8e00864e80000000000000000903bd1ff7b2e5ce80200000004000000043bd1ff207900249c908eec04000000583bd1ff603bd1ff4892796f04000000ac3bd1ff01000000901e77e917885ee9010000004d5cb1eb485cb1eb00908eec4892796f00000000000000000000000000004300cc55af6f983bd1ff00908eeceeac73e943000000640477e9901e77e9e6ac73e961705ce9f43bd1ff55000000ac3bd1ffeeac73e943000000640477e900005900e3225ce900908eec30528bec409181e900908eec430000005900000000528bec409181e9000043005500000078e388ec2100000009215ce901000000ce3fb8aae83cd1ff40420f00a3f6a7eb09215ce9f43bd1ff7e8ea6c309215ce9ed0ea8eb2100000075270000003289ec0000000030528becef665c74db0e42e911ac58e99daf58e9103dd1ff010000007e8ea6c31b000000385cd1ff385cd1ff02000000103dd1ff0300000087e26deae43cd1ff0200000001000000a31eb8aa020000007c3cd1ff18ac89ec1dac89ec0f000000fc94b4ec7c3cd1ff18ac89ec7e8ea6c3e83cd1ff884dd1ff741ab8aaa81ab8aa000000000700000004000000e43cd1ff3b19b8aa000000000000000000000000000000000000000000000000884dd1ff0000000001000000844dd1ff7e8ea6c3f065b4ec00fd0000205db8aa308789ec010000000000000004000000b8e78aec18ac89ec005db8aa2ceab2eb101082e935000000000000000800000001100000ba5bd1ff99000000b8e78aec205db8aa508789ec030000000000000004000000e2050000108789ec00000000d991aeece583aeec10d0acec10d0acec50d0acec6170705f70726f63657373333200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080a4ec00000000001000009dfe6feb00000000975673eb000000002516b8aa844dd1ff08000000a84dd1ff0000000000000000000000007c4dd1ff3b996feb00000000000000000000000000000000000000005015b8aad45cb8aadc5cb8aae85cb8aa804dd1ff0000000015b8aeec08000000ba5bd1ffd45bd1ffe05bd1ffee5bd1ff0f5cd1ff335cd1ff355cd1ff385cd1ff00000000535cd1ff6f5cd1ff825cd1ff9d5cd1ffbf5cd1ffd45cd1ffee5cd1ff015dd1ff1c5dd1ffe35ed1fffc5ed1ff465fd1ffc55fd1ff0000000010000000d6b0270006000000001000001100000064000000030000003400b8aa040000002000000005000000090000000700000000d0adec080000000000000009000000ec14b8aa0b000000752700000c000000752700000d000000752700000e000000752700001700000000000000190000007c4ed1ff1a0000001f0000001f000000de5fd1ff0f0000008c4ed1ff00000000000000000000000086da76325883c1a6b44d586d68c7843576386c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000636f6d2e6578616d706c652e7375646f67616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f3d2f73797374656d2f62696e2f6170705f70726f63657373333200414e44524f49445f444154413d2f6461746100444f574e4c4f41445f43414348453d2f646174612f636163686500414e44524f49445f534f434b45545f7a79676f74655f7365636f6e646172793d3900414e44524f49445f524f4f543d2f73797374656d00415345435f4d4f554e54504f494e543d2f6d6e742f6173656300414e44524f49445f424f4f544c4f474f3d3100414e44524f49445f4153534554533d2f73797374656d2f61707000424f4f54434c415353504154483d2f73797374656d2f6672616d65776f726b2f636f72652d6f6a2e6a61723a2f73797374656d2f6672616d65776f726b2f636f72652d6c69626172742e6a61723a2f73797374656d2f6672616d65776f726b2f636f6e7363727970742e6a61723a2f73797374656d2f6672616d65776f726b2f6f6b687474702e6a61723a2f73797374656d2f6672616d65776f726b2f6c65676163792d746573742e6a61723a2f73797374656d2f6672616d65776f726b2f626f756e6379636173746c652e6a61723a2f73797374656d2f6672616d65776f726b2f6578742e6a61723a2f73797374656d2f6672616d65776f726b2f6672616d65776f726b2e6a61723a2f73797374656d2f6672616d65776f726b2f74656c6570686f6e792d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f766f69702d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f696d732d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f6170616368652d786d6c2e6a61723a2f73797374656d2f6672616d65776f726b2f6f72672e6170616368652e687474702e6c65676163792e626f6f742e6a617200414e44524f49445f53544f524147453d2f73746f7261676500504154483d2f7362696e3a2f73797374656d2f7362696e3a2f73797374656d2f62696e3a2f73797374656d2f7862696e3a2f76656e646f722f62696e3a2f76656e646f722f7862696e0053595354454d534552564552434c415353504154483d2f73797374656d2f6672616d65776f726b2f73657276696365732e6a61723a2f73797374656d2f6672616d65776f726b2f65746865726e65742d736572766963652e6a61723a2f73797374656d2f6672616d65776f726b2f776966692d736572766963652e6a61720045585445524e414c5f53544f524147453d2f736463617264002f73797374656d2f62696e2f6170705f70726f636573733332000000000000000000
+function: start: 3a2121 end: 3a217a name: art_quick_invoke_stub_internal
+function: start: 3a66a5 end: 3a6787 name: art_quick_invoke_static_stub
+function: start: a7129 end: a72f1 name: art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)
+function: start: 2fbd35 end: 2fc789 name: art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::ArgArray*, art::JValue*, char const*)
+function: start: 2fcf75 end: 2fd88d name: art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned int)
+function: start: 2a089d end: 2a08bb name: art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobject*)
\ No newline at end of file
diff --git a/libbinderwrapper/Android.mk b/libbinderwrapper/Android.mk
index b38d262..c768373 100644
--- a/libbinderwrapper/Android.mk
+++ b/libbinderwrapper/Android.mk
@@ -41,7 +41,7 @@
 
 include $(BUILD_SHARED_LIBRARY)
 
-# libbinderwrapper_test_support shared library
+# libbinderwrapper_test_support static library
 # ========================================================
 
 include $(CLEAR_VARS)
@@ -59,4 +59,4 @@
   binder_test_base.cc \
   stub_binder_wrapper.cc \
 
-include $(BUILD_SHARED_LIBRARY)
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 39f8aba..cf31195 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -30,6 +30,23 @@
     "str_parms.c",
 ]
 
+cc_library_headers {
+    name: "libcutils_vndk_headers",
+    host_supported: true,
+    export_include_dirs: ["include_vndk"],
+}
+
+cc_library_headers {
+    name: "libcutils_headers",
+    host_supported: true,
+    export_include_dirs: ["include"],
+    target: {
+       windows: {
+          enabled: true,
+       },
+    },
+}
+
 cc_library {
     name: "libcutils",
     host_supported: true,
@@ -43,7 +60,7 @@
         "native_handle.c",
         "open_memstream.c",
         "record_stream.c",
-        "sched_policy.c",
+        "sched_policy.cpp",
         "sockets.cpp",
         "strdup16to8.c",
         "strdup8to16.c",
@@ -51,6 +68,7 @@
         "threads.c",
     ],
 
+
     target: {
         host: {
             srcs: ["dlmalloc_stubs.c"],
@@ -78,17 +96,16 @@
             srcs: libcutils_nonwindows_sources + [
                 "android_reboot.c",
                 "ashmem-dev.c",
-                "debugger.c",
                 "klog.cpp",
                 "partition_utils.c",
-                "properties.c",
+                "properties.cpp",
                 "qtaguid.c",
                 "trace-dev.c",
                 "uevent.c",
             ],
-
-            static_libs: ["libdebuggerd_client"],
-            export_static_lib_headers: ["libdebuggerd_client"],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
         },
 
         android_arm: {
@@ -121,14 +138,9 @@
     },
 
     shared_libs: ["liblog"],
-    product_variables: {
-        cpusets: {
-            cflags: ["-DUSE_CPUSETS"],
-        },
-        schedboost: {
-            cflags: ["-DUSE_SCHEDBOOST"],
-        },
-    },
+    header_libs: ["libcutils_headers"],
+    export_header_lib_headers: ["libcutils_headers"],
+
     cflags: [
         "-Werror",
         "-Wall",
@@ -136,9 +148,6 @@
     ],
 
     clang: true,
-    sanitize: {
-        misc_undefined: ["integer"],
-    },
 }
 
 subdirs = ["tests"]
diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c
index 159a9d4..06026d1 100644
--- a/libcutils/android_reboot.c
+++ b/libcutils/android_reboot.c
@@ -189,11 +189,56 @@
     free_entries(&ro_entries);
 }
 
+static void save_reboot_reason(int cmd, const char *arg)
+{
+    FILE *fp;
+    const char *reason = NULL;
+
+    fp = fopen(LAST_REBOOT_REASON_FILE, "w");
+    if (fp == NULL) {
+        KLOG_WARNING(TAG, "Error creating " LAST_REBOOT_REASON_FILE
+                     ": %s\n", strerror(errno));
+        return;
+    }
+    switch (cmd) {
+        case ANDROID_RB_RESTART:
+            reason = "restart";
+            break;
+
+        case ANDROID_RB_POWEROFF:
+            reason = "power-off";
+            break;
+
+        case ANDROID_RB_RESTART2:
+            reason = arg && strlen(arg) ? arg : "restart";
+            break;
+
+        case ANDROID_RB_THERMOFF:
+            reason = "thermal-shutdown";
+            break;
+
+        default:
+            fprintf(fp,"0x%08X\n", cmd);
+            break;
+    }
+
+    if (reason) {
+        if (fprintf(fp, "%s\n", reason) < 0) {
+             KLOG_WARNING(TAG, "Error writing " LAST_REBOOT_REASON_FILE
+                          ": %s\n", strerror(errno));
+        }
+    }
+
+    fclose(fp);
+}
+
 int android_reboot_with_callback(
     int cmd, int flags __unused, const char *arg,
     void (*cb_on_remount)(const struct mntent*))
 {
     int ret;
+
+    save_reboot_reason(cmd, arg);
     remount_ro(cb_on_remount);
     switch (cmd) {
         case ANDROID_RB_RESTART:
@@ -209,6 +254,10 @@
                            LINUX_REBOOT_CMD_RESTART2, arg);
             break;
 
+        case ANDROID_RB_THERMOFF:
+            ret = reboot(RB_POWER_OFF);
+            break;
+
         default:
             ret = -1;
     }
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
index db4ed11..92717c0 100644
--- a/libcutils/ashmem-dev.c
+++ b/libcutils/ashmem-dev.c
@@ -31,8 +31,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <android/log.h>
 #include <cutils/ashmem.h>
+#include <log/log.h>
 
 #define ASHMEM_DEVICE "/dev/ashmem"
 
diff --git a/libcutils/debugger.c b/libcutils/debugger.c
deleted file mode 100644
index c6bdd1a..0000000
--- a/libcutils/debugger.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <android/log.h>
-#include <cutils/debugger.h>
-#include <cutils/sockets.h>
-
-static int send_request(int sock_fd, void* msg_ptr, size_t msg_len) {
-  int result = 0;
-  if (TEMP_FAILURE_RETRY(write(sock_fd, msg_ptr, msg_len)) != (ssize_t) msg_len) {
-    result = -1;
-  } else {
-    char ack;
-    if (TEMP_FAILURE_RETRY(read(sock_fd, &ack, 1)) != 1) {
-      result = -1;
-    }
-  }
-  return result;
-}
-
-static int make_dump_request(debugger_action_t action, pid_t tid, int timeout_secs) {
-  debugger_msg_t msg;
-  memset(&msg, 0, sizeof(msg));
-  msg.tid = tid;
-  msg.action = action;
-
-  int sock_fd = socket_local_client(DEBUGGER_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
-      SOCK_STREAM | SOCK_CLOEXEC);
-  if (sock_fd < 0) {
-    return -1;
-  }
-
-  if (timeout_secs > 0) {
-    struct timeval tm;
-    tm.tv_sec = timeout_secs;
-    tm.tv_usec = 0;
-    if (setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tm, sizeof(tm)) == -1) {
-      ALOGE("WARNING: Cannot set receive timeout value on socket: %s", strerror(errno));
-    }
-
-    if (setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, &tm, sizeof(tm)) == -1) {
-      ALOGE("WARNING: Cannot set send timeout value on socket: %s", strerror(errno));
-    }
-  }
-
-  if (send_request(sock_fd, &msg, sizeof(msg)) < 0) {
-    close(sock_fd);
-    return -1;
-  }
-
-  return sock_fd;
-}
-
-int dump_backtrace_to_file(pid_t tid, int fd) {
-  return dump_backtrace_to_file_timeout(tid, fd, 0);
-}
-
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {
-  int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_BACKTRACE, tid, timeout_secs);
-  if (sock_fd < 0) {
-    return -1;
-  }
-
-  /* Write the data read from the socket to the fd. */
-  int result = 0;
-  char buffer[1024];
-  ssize_t n;
-  while ((n = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer)))) > 0) {
-    if (TEMP_FAILURE_RETRY(write(fd, buffer, n)) != n) {
-      result = -1;
-      break;
-    }
-  }
-  close(sock_fd);
-  return result;
-}
-
-int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen) {
-  return dump_tombstone_timeout(tid, pathbuf, pathlen, 0);
-}
-
-int dump_tombstone_timeout(pid_t tid, char* pathbuf, size_t pathlen, int timeout_secs) {
-  int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_TOMBSTONE, tid, timeout_secs);
-  if (sock_fd < 0) {
-    return -1;
-  }
-
-  /* Read the tombstone file name. */
-  char buffer[100]; /* This is larger than the largest tombstone path. */
-  int result = 0;
-  ssize_t n = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer) - 1));
-  if (n <= 0) {
-    result = -1;
-  } else {
-    if (pathbuf && pathlen) {
-      if (n >= (ssize_t) pathlen) {
-        n = pathlen - 1;
-      }
-      buffer[n] = '\0';
-      memcpy(pathbuf, buffer, n + 1);
-    }
-  }
-  close(sock_fd);
-  return result;
-}
diff --git a/libcutils/dlmalloc_stubs.c b/libcutils/dlmalloc_stubs.c
index 6c07bed..2cff9dd 100644
--- a/libcutils/dlmalloc_stubs.c
+++ b/libcutils/dlmalloc_stubs.c
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "dlmalloc-stubs"
 
-#include "android/log.h"
+#include "log/log.h"
 
 #define UNUSED __attribute__((__unused__))
 
diff --git a/libcutils/fs.c b/libcutils/fs.c
index c49233e..b253b1c 100644
--- a/libcutils/fs.c
+++ b/libcutils/fs.c
@@ -32,8 +32,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <android/log.h>
 #include <cutils/fs.h>
+#include <log/log.h>
 
 #define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
 #define BUF_SIZE 64
@@ -80,7 +80,7 @@
 create:
     create_result = prepare_as_dir
         ? TEMP_FAILURE_RETRY(mkdir(path, mode))
-        : TEMP_FAILURE_RETRY(open(path, O_CREAT | O_CLOEXEC | O_NOFOLLOW | O_RDONLY));
+        : TEMP_FAILURE_RETRY(open(path, O_CREAT | O_CLOEXEC | O_NOFOLLOW | O_RDONLY, 0644));
     if (create_result == -1) {
         if (errno != EEXIST) {
             ALOGE("Failed to %s(%s): %s",
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 99f97d1..c00f175 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -33,7 +33,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <android/log.h>
+#include <log/log.h>
 #include <private/android_filesystem_config.h>
 #include <utils/Compat.h>
 
@@ -162,7 +162,11 @@
     /* 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/bin/hw/android.hardware.wifi@1.0-service" },
+                                              "vendor/bin/hw/android.hardware.wifi@1.0-service" },
+
+    /* Support Bluetooth legacy hal accessing /sys/class/rfkill */
+    { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN),
+                                              "vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
 
     /* A non-privileged zygote that spawns isolated processes for web rendering. */
     { 0750,  AID_ROOT,      AID_ROOT,      CAP_MASK_LONG(CAP_SETUID) |
@@ -174,6 +178,10 @@
                                            CAP_MASK_LONG(CAP_SETPCAP),
                                               "system/bin/webview_zygote64" },
 
+    { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump32" },
+    { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump64" },
+    { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/debuggerd" },
+    { 00700, AID_ROOT,      AID_ROOT,      0, "system/bin/secilc" },
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/uncrypt" },
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/install-recovery.sh" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/*" },
@@ -189,6 +197,12 @@
     { 00750, AID_ROOT,      AID_SHELL,     0, "init*" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/fs_mgr" },
     { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "system/build.prop" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "vendor/build.prop" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "odm/build.prop" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "default.prop" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "vendor/default.prop" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "odm/default.prop" },
     { 00644, AID_ROOT,      AID_ROOT,      0, 0 },
 };
 
diff --git a/include/cutils/android_get_control_file.h b/libcutils/include/cutils/android_get_control_file.h
similarity index 100%
rename from include/cutils/android_get_control_file.h
rename to libcutils/include/cutils/android_get_control_file.h
diff --git a/include/cutils/android_reboot.h b/libcutils/include/cutils/android_reboot.h
similarity index 87%
rename from include/cutils/android_reboot.h
rename to libcutils/include/cutils/android_reboot.h
index a3861a0..2e3b429 100644
--- a/include/cutils/android_reboot.h
+++ b/libcutils/include/cutils/android_reboot.h
@@ -25,10 +25,14 @@
 #define ANDROID_RB_RESTART  0xDEAD0001
 #define ANDROID_RB_POWEROFF 0xDEAD0002
 #define ANDROID_RB_RESTART2 0xDEAD0003
+#define ANDROID_RB_THERMOFF 0xDEAD0004
 
 /* Properties */
 #define ANDROID_RB_PROPERTY "sys.powerctl"
 
+/* Android reboot reason stored in this file */
+#define LAST_REBOOT_REASON_FILE "/data/misc/reboot/last_reboot_reason"
+
 int android_reboot(int cmd, int flags, const char *arg);
 int android_reboot_with_callback(
     int cmd, int flags, const char *arg,
diff --git a/include/cutils/ashmem.h b/libcutils/include/cutils/ashmem.h
similarity index 100%
rename from include/cutils/ashmem.h
rename to libcutils/include/cutils/ashmem.h
diff --git a/include/cutils/atomic.h b/libcutils/include/cutils/atomic.h
similarity index 100%
rename from include/cutils/atomic.h
rename to libcutils/include/cutils/atomic.h
diff --git a/include/cutils/bitops.h b/libcutils/include/cutils/bitops.h
similarity index 100%
rename from include/cutils/bitops.h
rename to libcutils/include/cutils/bitops.h
diff --git a/include/cutils/compiler.h b/libcutils/include/cutils/compiler.h
similarity index 100%
rename from include/cutils/compiler.h
rename to libcutils/include/cutils/compiler.h
diff --git a/include/cutils/config_utils.h b/libcutils/include/cutils/config_utils.h
similarity index 100%
rename from include/cutils/config_utils.h
rename to libcutils/include/cutils/config_utils.h
diff --git a/include/cutils/fs.h b/libcutils/include/cutils/fs.h
similarity index 100%
rename from include/cutils/fs.h
rename to libcutils/include/cutils/fs.h
diff --git a/include/cutils/hashmap.h b/libcutils/include/cutils/hashmap.h
similarity index 100%
rename from include/cutils/hashmap.h
rename to libcutils/include/cutils/hashmap.h
diff --git a/include/cutils/iosched_policy.h b/libcutils/include/cutils/iosched_policy.h
similarity index 100%
rename from include/cutils/iosched_policy.h
rename to libcutils/include/cutils/iosched_policy.h
diff --git a/include/cutils/jstring.h b/libcutils/include/cutils/jstring.h
similarity index 100%
rename from include/cutils/jstring.h
rename to libcutils/include/cutils/jstring.h
diff --git a/include/cutils/klog.h b/libcutils/include/cutils/klog.h
similarity index 93%
rename from include/cutils/klog.h
rename to libcutils/include/cutils/klog.h
index c837edb..5ae6216 100644
--- a/include/cutils/klog.h
+++ b/libcutils/include/cutils/klog.h
@@ -23,7 +23,6 @@
 
 __BEGIN_DECLS
 
-int  klog_get_level(void);
 void klog_set_level(int level);
 
 void klog_write(int level, const char *fmt, ...)
@@ -44,6 +43,4 @@
 #define KLOG_INFO(tag,x...)    klog_write(KLOG_INFO_LEVEL, "<6>" tag ": " x)
 #define KLOG_DEBUG(tag,x...)   klog_write(KLOG_DEBUG_LEVEL, "<7>" tag ": " x)
 
-#define KLOG_DEFAULT_LEVEL  3  /* messages <= this level are logged */
-
 #endif
diff --git a/include/cutils/list.h b/libcutils/include/cutils/list.h
similarity index 100%
rename from include/cutils/list.h
rename to libcutils/include/cutils/list.h
diff --git a/include/cutils/log.h b/libcutils/include/cutils/log.h
similarity index 100%
rename from include/cutils/log.h
rename to libcutils/include/cutils/log.h
diff --git a/include/cutils/memory.h b/libcutils/include/cutils/memory.h
similarity index 100%
rename from include/cutils/memory.h
rename to libcutils/include/cutils/memory.h
diff --git a/include/cutils/misc.h b/libcutils/include/cutils/misc.h
similarity index 100%
rename from include/cutils/misc.h
rename to libcutils/include/cutils/misc.h
diff --git a/include/cutils/multiuser.h b/libcutils/include/cutils/multiuser.h
similarity index 70%
rename from include/cutils/multiuser.h
rename to libcutils/include/cutils/multiuser.h
index 7e7f815..5bd9c7b 100644
--- a/include/cutils/multiuser.h
+++ b/libcutils/include/cutils/multiuser.h
@@ -23,19 +23,20 @@
 extern "C" {
 #endif
 
-// NOTE: keep in sync with android.os.UserId
-
-#define MULTIUSER_APP_PER_USER_RANGE 100000
-#define MULTIUSER_FIRST_SHARED_APPLICATION_GID 50000
-#define MULTIUSER_FIRST_APPLICATION_UID 10000
-
 typedef uid_t userid_t;
 typedef uid_t appid_t;
 
 extern userid_t multiuser_get_user_id(uid_t uid);
 extern appid_t multiuser_get_app_id(uid_t uid);
-extern uid_t multiuser_get_uid(userid_t userId, appid_t appId);
-extern appid_t multiuser_get_shared_app_gid(uid_t uid);
+
+extern uid_t multiuser_get_uid(userid_t user_id, appid_t app_id);
+
+extern gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id);
+extern gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id);
+extern gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id);
+
+/* TODO: switch callers over to multiuser_get_shared_gid() */
+extern gid_t multiuser_get_shared_app_gid(uid_t uid);
 
 #ifdef __cplusplus
 }
diff --git a/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
similarity index 100%
rename from include/cutils/native_handle.h
rename to libcutils/include/cutils/native_handle.h
diff --git a/include/cutils/open_memstream.h b/libcutils/include/cutils/open_memstream.h
similarity index 100%
rename from include/cutils/open_memstream.h
rename to libcutils/include/cutils/open_memstream.h
diff --git a/include/cutils/partition_utils.h b/libcutils/include/cutils/partition_utils.h
similarity index 100%
rename from include/cutils/partition_utils.h
rename to libcutils/include/cutils/partition_utils.h
diff --git a/include/cutils/properties.h b/libcutils/include/cutils/properties.h
similarity index 75%
rename from include/cutils/properties.h
rename to libcutils/include/cutils/properties.h
index adf670b..b45f58f 100644
--- a/include/cutils/properties.h
+++ b/libcutils/include/cutils/properties.h
@@ -43,7 +43,12 @@
 ** If the property read fails or returns an empty value, the default
 ** value is used (if nonnull).
 */
-int property_get(const char *key, char *value, const char *default_value);
+int property_get(const char *key, char *value, const char *default_value)
+/* Sometimes we use not-Bionic with this, so we need this check. */
+#if defined(__BIONIC_FORTIFY)
+        __overloadable __RENAME_CLANG(property_get)
+#endif
+        ;
 
 /* property_get_bool: returns the value of key coerced into a
 ** boolean. If the property is not set, then the default value is returned.
@@ -106,14 +111,40 @@
 /* property_set: returns 0 on success, < 0 on failure
 */
 int property_set(const char *key, const char *value);
-    
-int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);    
 
-#if defined(__BIONIC_FORTIFY) && !defined(__clang__)
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);
+
+#if defined(__BIONIC_FORTIFY)
+#define __property_get_err_str "property_get() called with too small of a buffer"
+
+#if defined(__clang__)
+
+/* Some projects use -Weverything; enable_if is clang-specific.
+** FIXME: This is marked used because we'll otherwise get complaints about an
+** unused static function. This is more robust than marking it unused, since
+** -Wused-but-marked-unused is a thing that will complain if this function is
+** actually used, thus making FORTIFY noisier when an error happens. It's going
+** to go away anyway during our FORTIFY cleanup.
+**/
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgcc-compat"
+__BIONIC_ERROR_FUNCTION_VISIBILITY
+int property_get(const char *key, char *value, const char *default_value)
+        __overloadable
+        __enable_if(__bos(value) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
+                    __bos(value) < PROPERTY_VALUE_MAX, __property_get_err_str)
+        __errorattr(__property_get_err_str)
+        __attribute__((used));
+#pragma clang diagnostic pop
+
+/* No object size? No FORTIFY.
+*/
+
+#else /* defined(__clang__) */
 
 extern int __property_get_real(const char *, char *, const char *)
     __asm__(__USER_LABEL_PREFIX__ "property_get");
-__errordecl(__property_get_too_small_error, "property_get() called with too small of a buffer");
+__errordecl(__property_get_too_small_error, __property_get_err_str);
 
 __BIONIC_FORTIFY_INLINE
 int property_get(const char *key, char *value, const char *default_value) {
@@ -124,7 +155,10 @@
     return __property_get_real(key, value, default_value);
 }
 
-#endif
+#endif /* defined(__clang__) */
+
+#undef __property_get_err_str
+#endif /* defined(__BIONIC_FORTIFY) */
 
 #ifdef __cplusplus
 }
diff --git a/include/cutils/qtaguid.h b/libcutils/include/cutils/qtaguid.h
similarity index 100%
rename from include/cutils/qtaguid.h
rename to libcutils/include/cutils/qtaguid.h
diff --git a/include/cutils/record_stream.h b/libcutils/include/cutils/record_stream.h
similarity index 100%
rename from include/cutils/record_stream.h
rename to libcutils/include/cutils/record_stream.h
diff --git a/include/cutils/sched_policy.h b/libcutils/include/cutils/sched_policy.h
similarity index 83%
rename from include/cutils/sched_policy.h
rename to libcutils/include/cutils/sched_policy.h
index 591bd44..15391d9 100644
--- a/include/cutils/sched_policy.h
+++ b/libcutils/include/cutils/sched_policy.h
@@ -17,10 +17,27 @@
 #ifndef __CUTILS_SCHED_POLICY_H
 #define __CUTILS_SCHED_POLICY_H
 
+#include <stdbool.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/*
+ * Check if Linux kernel enables CPUSETS feature.
+ *
+ * Return value: 1 if Linux kernel CONFIG_CPUSETS=y; 0 otherwise.
+ */
+extern bool cpusets_enabled();
+
+/*
+ * Check if Linux kernel enables SCHEDTUNE feature (only available in Android
+ * common kernel or Linaro LSK, not in mainline Linux as of v4.9)
+ *
+ * Return value: 1 if Linux kernel CONFIG_SCHEDTUNE=y; 0 otherwise.
+ */
+extern bool schedboost_enabled();
+
 /* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */
 typedef enum {
     SP_DEFAULT    = -1,
diff --git a/include/cutils/sockets.h b/libcutils/include/cutils/sockets.h
similarity index 100%
rename from include/cutils/sockets.h
rename to libcutils/include/cutils/sockets.h
diff --git a/include/cutils/str_parms.h b/libcutils/include/cutils/str_parms.h
similarity index 100%
rename from include/cutils/str_parms.h
rename to libcutils/include/cutils/str_parms.h
diff --git a/include/cutils/threads.h b/libcutils/include/cutils/threads.h
similarity index 100%
rename from include/cutils/threads.h
rename to libcutils/include/cutils/threads.h
diff --git a/include/cutils/trace.h b/libcutils/include/cutils/trace.h
similarity index 98%
rename from include/cutils/trace.h
rename to libcutils/include/cutils/trace.h
index 0f00417..fcbdc9b 100644
--- a/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -71,7 +71,8 @@
 #define ATRACE_TAG_SYSTEM_SERVER    (1<<19)
 #define ATRACE_TAG_DATABASE         (1<<20)
 #define ATRACE_TAG_NETWORK          (1<<21)
-#define ATRACE_TAG_LAST             ATRACE_TAG_NETWORK
+#define ATRACE_TAG_ADB              (1<<22)
+#define ATRACE_TAG_LAST             ATRACE_TAG_ADB
 
 // Reserved for initialization.
 #define ATRACE_TAG_NOT_READY        (1ULL<<63)
diff --git a/include/cutils/uevent.h b/libcutils/include/cutils/uevent.h
similarity index 100%
rename from include/cutils/uevent.h
rename to libcutils/include/cutils/uevent.h
diff --git a/libcutils/include_vndk/cutils/android_get_control_file.h b/libcutils/include_vndk/cutils/android_get_control_file.h
new file mode 120000
index 0000000..70d6a3b
--- /dev/null
+++ b/libcutils/include_vndk/cutils/android_get_control_file.h
@@ -0,0 +1 @@
+../../include/cutils/android_get_control_file.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/android_reboot.h b/libcutils/include_vndk/cutils/android_reboot.h
new file mode 120000
index 0000000..9e1bf4c
--- /dev/null
+++ b/libcutils/include_vndk/cutils/android_reboot.h
@@ -0,0 +1 @@
+../../include/cutils/android_reboot.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/ashmem.h b/libcutils/include_vndk/cutils/ashmem.h
new file mode 120000
index 0000000..5c07beb
--- /dev/null
+++ b/libcutils/include_vndk/cutils/ashmem.h
@@ -0,0 +1 @@
+../../include/cutils/ashmem.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/atomic.h b/libcutils/include_vndk/cutils/atomic.h
new file mode 120000
index 0000000..f4f14fe
--- /dev/null
+++ b/libcutils/include_vndk/cutils/atomic.h
@@ -0,0 +1 @@
+../../include/cutils/atomic.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/bitops.h b/libcutils/include_vndk/cutils/bitops.h
new file mode 120000
index 0000000..edbd60c
--- /dev/null
+++ b/libcutils/include_vndk/cutils/bitops.h
@@ -0,0 +1 @@
+../../include/cutils/bitops.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/compiler.h b/libcutils/include_vndk/cutils/compiler.h
new file mode 120000
index 0000000..08ebc10
--- /dev/null
+++ b/libcutils/include_vndk/cutils/compiler.h
@@ -0,0 +1 @@
+../../include/cutils/compiler.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/config_utils.h b/libcutils/include_vndk/cutils/config_utils.h
new file mode 120000
index 0000000..e011738
--- /dev/null
+++ b/libcutils/include_vndk/cutils/config_utils.h
@@ -0,0 +1 @@
+../../include/cutils/config_utils.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/fs.h b/libcutils/include_vndk/cutils/fs.h
new file mode 120000
index 0000000..576bfa3
--- /dev/null
+++ b/libcutils/include_vndk/cutils/fs.h
@@ -0,0 +1 @@
+../../include/cutils/fs.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/hashmap.h b/libcutils/include_vndk/cutils/hashmap.h
new file mode 120000
index 0000000..6b18406
--- /dev/null
+++ b/libcutils/include_vndk/cutils/hashmap.h
@@ -0,0 +1 @@
+../../include/cutils/hashmap.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/iosched_policy.h b/libcutils/include_vndk/cutils/iosched_policy.h
new file mode 120000
index 0000000..26cf333
--- /dev/null
+++ b/libcutils/include_vndk/cutils/iosched_policy.h
@@ -0,0 +1 @@
+../../include/cutils/iosched_policy.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/jstring.h b/libcutils/include_vndk/cutils/jstring.h
new file mode 120000
index 0000000..f3fd546
--- /dev/null
+++ b/libcutils/include_vndk/cutils/jstring.h
@@ -0,0 +1 @@
+../../include/cutils/jstring.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/klog.h b/libcutils/include_vndk/cutils/klog.h
new file mode 120000
index 0000000..8ca85ff
--- /dev/null
+++ b/libcutils/include_vndk/cutils/klog.h
@@ -0,0 +1 @@
+../../include/cutils/klog.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/list.h b/libcutils/include_vndk/cutils/list.h
new file mode 120000
index 0000000..9fa4c90
--- /dev/null
+++ b/libcutils/include_vndk/cutils/list.h
@@ -0,0 +1 @@
+../../include/cutils/list.h
\ No newline at end of file
diff --git a/debuggerd/test/selinux_fake.cpp b/libcutils/include_vndk/cutils/log.h
similarity index 64%
copy from debuggerd/test/selinux_fake.cpp
copy to libcutils/include_vndk/cutils/log.h
index acdd0a9..ae74024 100644
--- a/debuggerd/test/selinux_fake.cpp
+++ b/libcutils/include_vndk/cutils/log.h
@@ -1,5 +1,6 @@
+/*Special log.h file for VNDK linking modules*/
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2005-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.
@@ -12,8 +13,9 @@
  * 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.
- */
-
-extern "C" int selinux_android_restorecon(const char*, unsigned int) {
-  return 0;
-}
+*/
+#ifndef _LIBS_CUTIL_LOG_H
+#define _LIBS_CUTIL_LOG_H
+#warning "Deprecated: don't include cutils/log.h, use either android/log.h or log/log.h"
+#include <log/log.h>
+#endif /* _LIBS_CUTIL_LOG_H */
diff --git a/libcutils/include_vndk/cutils/memory.h b/libcutils/include_vndk/cutils/memory.h
new file mode 120000
index 0000000..e0e7abc
--- /dev/null
+++ b/libcutils/include_vndk/cutils/memory.h
@@ -0,0 +1 @@
+../../include/cutils/memory.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/misc.h b/libcutils/include_vndk/cutils/misc.h
new file mode 120000
index 0000000..db09eb5
--- /dev/null
+++ b/libcutils/include_vndk/cutils/misc.h
@@ -0,0 +1 @@
+../../include/cutils/misc.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/multiuser.h b/libcutils/include_vndk/cutils/multiuser.h
new file mode 120000
index 0000000..524111f
--- /dev/null
+++ b/libcutils/include_vndk/cutils/multiuser.h
@@ -0,0 +1 @@
+../../include/cutils/multiuser.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/native_handle.h b/libcutils/include_vndk/cutils/native_handle.h
new file mode 120000
index 0000000..e324d4e
--- /dev/null
+++ b/libcutils/include_vndk/cutils/native_handle.h
@@ -0,0 +1 @@
+../../include/cutils/native_handle.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/open_memstream.h b/libcutils/include_vndk/cutils/open_memstream.h
new file mode 120000
index 0000000..c894084
--- /dev/null
+++ b/libcutils/include_vndk/cutils/open_memstream.h
@@ -0,0 +1 @@
+../../include/cutils/open_memstream.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/partition_utils.h b/libcutils/include_vndk/cutils/partition_utils.h
new file mode 120000
index 0000000..d9734c8
--- /dev/null
+++ b/libcutils/include_vndk/cutils/partition_utils.h
@@ -0,0 +1 @@
+../../include/cutils/partition_utils.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/properties.h b/libcutils/include_vndk/cutils/properties.h
new file mode 120000
index 0000000..d56118e
--- /dev/null
+++ b/libcutils/include_vndk/cutils/properties.h
@@ -0,0 +1 @@
+../../include/cutils/properties.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/qtaguid.h b/libcutils/include_vndk/cutils/qtaguid.h
new file mode 120000
index 0000000..bc02441
--- /dev/null
+++ b/libcutils/include_vndk/cutils/qtaguid.h
@@ -0,0 +1 @@
+../../include/cutils/qtaguid.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/record_stream.h b/libcutils/include_vndk/cutils/record_stream.h
new file mode 120000
index 0000000..8de6494
--- /dev/null
+++ b/libcutils/include_vndk/cutils/record_stream.h
@@ -0,0 +1 @@
+../../include/cutils/record_stream.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/sched_policy.h b/libcutils/include_vndk/cutils/sched_policy.h
new file mode 120000
index 0000000..ddebdd0
--- /dev/null
+++ b/libcutils/include_vndk/cutils/sched_policy.h
@@ -0,0 +1 @@
+../../include/cutils/sched_policy.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/sockets.h b/libcutils/include_vndk/cutils/sockets.h
new file mode 120000
index 0000000..585250c
--- /dev/null
+++ b/libcutils/include_vndk/cutils/sockets.h
@@ -0,0 +1 @@
+../../include/cutils/sockets.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/str_parms.h b/libcutils/include_vndk/cutils/str_parms.h
new file mode 120000
index 0000000..9c79a8f
--- /dev/null
+++ b/libcutils/include_vndk/cutils/str_parms.h
@@ -0,0 +1 @@
+../../include/cutils/str_parms.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/threads.h b/libcutils/include_vndk/cutils/threads.h
new file mode 120000
index 0000000..99330ff
--- /dev/null
+++ b/libcutils/include_vndk/cutils/threads.h
@@ -0,0 +1 @@
+../../include/cutils/threads.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/trace.h b/libcutils/include_vndk/cutils/trace.h
new file mode 120000
index 0000000..b12e140
--- /dev/null
+++ b/libcutils/include_vndk/cutils/trace.h
@@ -0,0 +1 @@
+../../include/cutils/trace.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/uevent.h b/libcutils/include_vndk/cutils/uevent.h
new file mode 120000
index 0000000..451283a
--- /dev/null
+++ b/libcutils/include_vndk/cutils/uevent.h
@@ -0,0 +1 @@
+../../include/cutils/uevent.h
\ No newline at end of file
diff --git a/libcutils/klog.cpp b/libcutils/klog.cpp
index 4bad28a..d301276 100644
--- a/libcutils/klog.cpp
+++ b/libcutils/klog.cpp
@@ -27,11 +27,7 @@
 #include <cutils/android_get_control_file.h>
 #include <cutils/klog.h>
 
-static int klog_level = KLOG_DEFAULT_LEVEL;
-
-int klog_get_level(void) {
-    return klog_level;
-}
+static int klog_level = KLOG_INFO_LEVEL;
 
 void klog_set_level(int level) {
     klog_level = level;
diff --git a/libcutils/multiuser.c b/libcutils/multiuser.c
index 0f4427b..08d4d6c 100644
--- a/libcutils/multiuser.c
+++ b/libcutils/multiuser.c
@@ -15,21 +15,44 @@
  */
 
 #include <cutils/multiuser.h>
+#include <private/android_filesystem_config.h>
 
 userid_t multiuser_get_user_id(uid_t uid) {
-    return uid / MULTIUSER_APP_PER_USER_RANGE;
+    return uid / AID_USER_OFFSET;
 }
 
 appid_t multiuser_get_app_id(uid_t uid) {
-    return uid % MULTIUSER_APP_PER_USER_RANGE;
+    return uid % AID_USER_OFFSET;
 }
 
-uid_t multiuser_get_uid(userid_t userId, appid_t appId) {
-    return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE);
+uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) {
+    return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
 }
 
-appid_t multiuser_get_shared_app_gid(uid_t id) {
-  return MULTIUSER_FIRST_SHARED_APPLICATION_GID + (id % MULTIUSER_APP_PER_USER_RANGE)
-          - MULTIUSER_FIRST_APPLICATION_UID;
+gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) {
+    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START);
+    } else {
+        return -1;
+    }
+}
 
+gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) {
+    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START);
+    } else {
+        return -1;
+    }
+}
+
+gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id) {
+    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_SHARED_GID_START);
+    } else {
+        return -1;
+    }
+}
+
+gid_t multiuser_get_shared_app_gid(uid_t uid) {
+    return multiuser_get_shared_gid(multiuser_get_user_id(uid), multiuser_get_app_id(uid));
 }
diff --git a/libcutils/properties.c b/libcutils/properties.cpp
similarity index 73%
rename from libcutils/properties.c
rename to libcutils/properties.cpp
index 5aa6371..d2645e6 100644
--- a/libcutils/properties.c
+++ b/libcutils/properties.cpp
@@ -26,9 +26,9 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <android/log.h>
 #include <cutils/properties.h>
 #include <cutils/sockets.h>
+#include <log/log.h>
 
 int8_t property_get_bool(const char *key, int8_t default_value) {
     if (!key) {
@@ -36,7 +36,7 @@
     }
 
     int8_t result = default_value;
-    char buf[PROPERTY_VALUE_MAX] = {'\0',};
+    char buf[PROPERTY_VALUE_MAX] = {'\0'};
 
     int len = property_get(key, buf, "");
     if (len == 1) {
@@ -47,7 +47,7 @@
             result = true;
         }
     } else if (len > 1) {
-         if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) {
+        if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) {
             result = false;
         } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) {
             result = true;
@@ -59,13 +59,13 @@
 
 // Convert string property to int (default if fails); return default value if out of bounds
 static intmax_t property_get_imax(const char *key, intmax_t lower_bound, intmax_t upper_bound,
-        intmax_t default_value) {
+                                  intmax_t default_value) {
     if (!key) {
         return default_value;
     }
 
     intmax_t result = default_value;
-    char buf[PROPERTY_VALUE_MAX] = {'\0',};
+    char buf[PROPERTY_VALUE_MAX] = {'\0'};
     char *end = NULL;
 
     int len = property_get(key, buf, "");
@@ -74,7 +74,7 @@
         errno = 0;
 
         // Infer base automatically
-        result = strtoimax(buf, &end, /*base*/0);
+        result = strtoimax(buf, &end, /*base*/ 0);
         if ((result == INTMAX_MIN || result == INTMAX_MAX) && errno == ERANGE) {
             // Over or underflow
             result = default_value;
@@ -86,8 +86,8 @@
         } else if (end == buf) {
             // Numeric conversion failed
             result = default_value;
-            ALOGV("%s(%s,%" PRIdMAX ") - numeric conversion failed",
-                    __FUNCTION__, key, default_value);
+            ALOGV("%s(%s,%" PRIdMAX ") - numeric conversion failed", __FUNCTION__, key,
+                  default_value);
         }
 
         errno = tmp;
@@ -107,50 +107,38 @@
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
-int property_set(const char *key, const char *value)
-{
+int property_set(const char *key, const char *value) {
     return __system_property_set(key, value);
 }
 
-int property_get(const char *key, char *value, const char *default_value)
-{
-    int len;
-
-    len = __system_property_get(key, value);
-    if(len > 0) {
+int property_get(const char *key, char *value, const char *default_value) {
+    int len = __system_property_get(key, value);
+    if (len > 0) {
         return len;
     }
-    if(default_value) {
-        len = strlen(default_value);
-        if (len >= PROPERTY_VALUE_MAX) {
-            len = PROPERTY_VALUE_MAX - 1;
-        }
+    if (default_value) {
+        len = strnlen(default_value, PROPERTY_VALUE_MAX - 1);
         memcpy(value, default_value, len);
         value[len] = '\0';
     }
     return len;
 }
 
-struct property_list_callback_data
-{
-    void (*propfn)(const char *key, const char *value, void *cookie);
-    void *cookie;
+struct callback_data {
+    void (*callback)(const char* name, const char* value, void* cookie);
+    void* cookie;
 };
 
-static void property_list_callback(const prop_info *pi, void *cookie)
-{
-    char name[PROP_NAME_MAX];
-    char value[PROP_VALUE_MAX];
-    struct property_list_callback_data *data = cookie;
-
-    __system_property_read(pi, name, value);
-    data->propfn(name, value, data->cookie);
+static void trampoline(void* raw_data, const char* name, const char* value, unsigned /*serial*/) {
+    callback_data* data = reinterpret_cast<callback_data*>(raw_data);
+    data->callback(name, value, data->cookie);
 }
 
-int property_list(
-        void (*propfn)(const char *key, const char *value, void *cookie),
-        void *cookie)
-{
-    struct property_list_callback_data data = { propfn, cookie };
+static void property_list_callback(const prop_info* pi, void* data) {
+    __system_property_read_callback(pi, trampoline, data);
+}
+
+int property_list(void (*fn)(const char* name, const char* value, void* cookie), void* cookie) {
+    callback_data data = { fn, cookie };
     return __system_property_foreach(property_list_callback, &data);
 }
diff --git a/libcutils/qtaguid.c b/libcutils/qtaguid.c
index ae5a503..22b8325 100644
--- a/libcutils/qtaguid.c
+++ b/libcutils/qtaguid.c
@@ -26,7 +26,7 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <android/log.h>
+#include <log/log.h>
 #include <cutils/qtaguid.h>
 
 static const char* CTRL_PROCPATH = "/proc/net/xt_qtaguid/ctrl";
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.cpp
similarity index 73%
rename from libcutils/sched_policy.c
rename to libcutils/sched_policy.cpp
index 5c5f3a5..7e3ad59 100644
--- a/libcutils/sched_policy.c
+++ b/libcutils/sched_policy.cpp
@@ -58,13 +58,11 @@
 static int bg_cgroup_fd = -1;
 static int fg_cgroup_fd = -1;
 
-#ifdef USE_CPUSETS
 // File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
 static int system_bg_cpuset_fd = -1;
 static int bg_cpuset_fd = -1;
 static int fg_cpuset_fd = -1;
 static int ta_cpuset_fd = -1; // special cpuset for top app
-#endif
 
 // File descriptors open to /dev/stune/../tasks, setup by initialize, or -1 on error
 static int bg_schedboost_fd = -1;
@@ -106,8 +104,53 @@
     return 0;
 }
 
-static void __initialize(void) {
-    char* filename;
+/*
+    If CONFIG_CPUSETS for Linux kernel is set, "tasks" can be found under
+    /dev/cpuset mounted in init.rc; otherwise, that file does not exist
+    even though the directory, /dev/cpuset, is still created (by init.rc).
+
+    A couple of other candidates (under cpuset mount directory):
+        notify_on_release
+        release_agent
+
+    Yet another way to decide if cpuset is enabled is to parse
+    /proc/self/status and search for lines begin with "Mems_allowed".
+
+    If CONFIG_PROC_PID_CPUSET is set, the existence "/proc/self/cpuset" can
+    be used to decide if CONFIG_CPUSETS is set, so we don't have a dependency
+    on where init.rc mounts cpuset. That's why we'd better require this
+    configuration be set if CONFIG_CPUSETS is set.
+
+    With runtime check using the following function, build time
+    variables like ENABLE_CPUSETS (used in Android.mk) or cpusets (used
+    in Android.bp) are not needed.
+ */
+
+bool cpusets_enabled() {
+    static bool enabled = (access("/dev/cpuset/tasks", F_OK) == 0);
+
+    return enabled;
+}
+
+/*
+    Similar to CONFIG_CPUSETS above, but with a different configuration
+    CONFIG_SCHEDTUNE that's in Android common Linux kernel and Linaro
+    Stable Kernel (LSK), but not in mainline Linux as of v4.9.
+
+    With runtime check using the following function, build time
+    variables like ENABLE_SCHEDBOOST (used in Android.mk) or schedboost
+    (used in Android.bp) are not needed.
+
+ */
+
+bool schedboost_enabled() {
+    static bool enabled = (access("/dev/stune/tasks", F_OK) == 0);
+
+    return enabled;
+}
+
+static void __initialize() {
+    const char* filename;
     if (!access("/dev/cpuctl/tasks", W_OK)) {
         __sys_supports_schedgroups = 1;
 
@@ -126,28 +169,28 @@
         __sys_supports_schedgroups = 0;
     }
 
-#ifdef USE_CPUSETS
-    if (!access("/dev/cpuset/tasks", W_OK)) {
+    if (cpusets_enabled()) {
+        if (!access("/dev/cpuset/tasks", W_OK)) {
 
-        filename = "/dev/cpuset/foreground/tasks";
-        fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
-        filename = "/dev/cpuset/background/tasks";
-        bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
-        filename = "/dev/cpuset/system-background/tasks";
-        system_bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
-        filename = "/dev/cpuset/top-app/tasks";
-        ta_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+            filename = "/dev/cpuset/foreground/tasks";
+            fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+            filename = "/dev/cpuset/background/tasks";
+            bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+            filename = "/dev/cpuset/system-background/tasks";
+            system_bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+            filename = "/dev/cpuset/top-app/tasks";
+            ta_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
 
-#ifdef USE_SCHEDBOOST
-        filename = "/dev/stune/top-app/tasks";
-        ta_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
-        filename = "/dev/stune/foreground/tasks";
-        fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
-        filename = "/dev/stune/background/tasks";
-        bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
-#endif
+            if (schedboost_enabled()) {
+                filename = "/dev/stune/top-app/tasks";
+                ta_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+                filename = "/dev/stune/foreground/tasks";
+                fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+                filename = "/dev/stune/background/tasks";
+                bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+            }
+        }
     }
-#endif
 
     char buf[64];
     snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", getpid());
@@ -235,33 +278,34 @@
 
     if (__sys_supports_schedgroups) {
         char grpBuf[32];
-#ifdef USE_CPUSETS
-        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;
+
+        if (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 {
-            errno = ERANGE;
-            return -1;
+            if (getCGroupSubsys(tid, "cpu", grpBuf, sizeof(grpBuf)) < 0)
+                return -1;
+            if (grpBuf[0] == '\0') {
+                *policy = SP_FOREGROUND;
+            } else if (!strcmp(grpBuf, "bg_non_interactive")) {
+                *policy = SP_BACKGROUND;
+            } else {
+                errno = ERANGE;
+                return -1;
+            }
         }
-#else
-        if (getCGroupSubsys(tid, "cpu", grpBuf, sizeof(grpBuf)) < 0)
-            return -1;
-        if (grpBuf[0] == '\0') {
-            *policy = SP_FOREGROUND;
-        } else if (!strcmp(grpBuf, "bg_non_interactive")) {
-            *policy = SP_BACKGROUND;
-        } else {
-            errno = ERANGE;
-            return -1;
-        }
-#endif
     } else {
         int rc = sched_getscheduler(tid);
         if (rc < 0)
@@ -281,9 +325,10 @@
 int set_cpuset_policy(int tid, SchedPolicy policy)
 {
     // in the absence of cpusets, use the old sched policy
-#ifndef USE_CPUSETS
-    return set_sched_policy(tid, policy);
-#else
+    if (!cpusets_enabled()) {
+        return set_sched_policy(tid, policy);
+    }
+
     if (tid == 0) {
         tid = gettid();
     }
@@ -320,15 +365,14 @@
             return -errno;
     }
 
-#ifdef USE_SCHEDBOOST
-    if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
-        if (errno != ESRCH && errno != ENOENT)
-            return -errno;
+    if (schedboost_enabled()) {
+        if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
+            if (errno != ESRCH && errno != ENOENT)
+                return -errno;
+        }
     }
-#endif
 
     return 0;
-#endif
 }
 
 static void set_timerslack_ns(int tid, unsigned long long slack) {
@@ -420,18 +464,17 @@
             break;
         }
 
-
         if (add_tid_to_cgroup(tid, fd) != 0) {
             if (errno != ESRCH && errno != ENOENT)
                 return -errno;
         }
 
-#ifdef USE_SCHEDBOOST
-        if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
-            if (errno != ESRCH && errno != ENOENT)
-                return -errno;
+        if (schedboost_enabled()) {
+            if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
+                if (errno != ESRCH && errno != ENOENT)
+                    return -errno;
+            }
         }
-#endif
     } else {
         struct sched_param param;
 
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
index 5a14a5c..e91f358 100644
--- a/libcutils/sockets_unix.cpp
+++ b/libcutils/sockets_unix.cpp
@@ -25,9 +25,9 @@
 #include <time.h>
 #include <unistd.h>
 
-#include <android/log.h>
 #include <cutils/android_get_control_file.h>
 #include <cutils/sockets.h>
+#include <log/log.h>
 
 #include "android_get_control_env.h"
 
diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c
index 6bb7e58..8dafded 100644
--- a/libcutils/str_parms.c
+++ b/libcutils/str_parms.c
@@ -24,10 +24,10 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <android/log.h>
 #include <cutils/hashmap.h>
 #include <cutils/memory.h>
 #include <cutils/str_parms.h>
+#include <log/log.h>
 
 #define UNUSED __attribute__((unused))
 
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index abe3c21..718d76b 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -26,7 +26,8 @@
                 "trace-dev_test.cpp",
                 "test_str_parms.cpp",
                 "android_get_control_socket_test.cpp",
-                "android_get_control_file_test.cpp"
+                "android_get_control_file_test.cpp",
+                "multiuser_test.cpp"
             ],
         },
 
@@ -45,6 +46,12 @@
             suffix: "64",
         },
     },
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
 }
 
 test_libraries = [
diff --git a/libcutils/tests/PropertiesTest.cpp b/libcutils/tests/PropertiesTest.cpp
index f0cdffd..7921972 100644
--- a/libcutils/tests/PropertiesTest.cpp
+++ b/libcutils/tests/PropertiesTest.cpp
@@ -159,19 +159,68 @@
 
 TEST_F(PropertiesTest, GetString) {
 
-    // Try to use a default value that's too long => set fails
+    // Try to use a default value that's too long => get truncates the value
     {
         ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
 
-        std::string maxLengthString = std::string(PROPERTY_VALUE_MAX-1, 'a');
+        std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'a');
         std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
 
         // Expect that the value is truncated since it's too long (by 1)
         int len = property_get(PROPERTY_TEST_KEY, mValue, oneLongerString.c_str());
-        EXPECT_EQ(PROPERTY_VALUE_MAX-1, len);
+        EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
         EXPECT_STREQ(maxLengthString.c_str(), mValue);
         ResetValue();
     }
+
+    // Try to use a default value that's the max length => get succeeds
+    {
+        ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+        std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'b');
+
+        // Expect that the value matches maxLengthString
+        int len = property_get(PROPERTY_TEST_KEY, mValue, maxLengthString.c_str());
+        EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
+        EXPECT_STREQ(maxLengthString.c_str(), mValue);
+        ResetValue();
+    }
+
+    // Try to use a default value of length one => get succeeds
+    {
+        ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+        std::string oneCharString = std::string(1, 'c');
+
+        // Expect that the value matches oneCharString
+        int len = property_get(PROPERTY_TEST_KEY, mValue, oneCharString.c_str());
+        EXPECT_EQ(1, len);
+        EXPECT_STREQ(oneCharString.c_str(), mValue);
+        ResetValue();
+    }
+
+    // Try to use a default value of length zero => get succeeds
+    {
+        ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+        std::string zeroCharString = std::string(0, 'd');
+
+        // Expect that the value matches oneCharString
+        int len = property_get(PROPERTY_TEST_KEY, mValue, zeroCharString.c_str());
+        EXPECT_EQ(0, len);
+        EXPECT_STREQ(zeroCharString.c_str(), mValue);
+        ResetValue();
+    }
+
+    // Try to use a NULL default value => get returns 0
+    {
+        ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+        // Expect a return value of 0
+        int len = property_get(PROPERTY_TEST_KEY, mValue, NULL);
+        EXPECT_EQ(0, len);
+        ResetValue();
+    }
 }
 
 TEST_F(PropertiesTest, GetBool) {
diff --git a/libcutils/tests/multiuser_test.cpp b/libcutils/tests/multiuser_test.cpp
new file mode 100644
index 0000000..ae5c416
--- /dev/null
+++ b/libcutils/tests/multiuser_test.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 <cutils/multiuser.h>
+#include <gtest/gtest.h>
+
+static constexpr auto ERR_GID = static_cast<gid_t>(-1);
+
+TEST(MultiuserTest, TestMerge) {
+    EXPECT_EQ(0U, multiuser_get_uid(0, 0));
+    EXPECT_EQ(1000U, multiuser_get_uid(0, 1000));
+    EXPECT_EQ(10000U, multiuser_get_uid(0, 10000));
+    EXPECT_EQ(50000U, multiuser_get_uid(0, 50000));
+    EXPECT_EQ(1000000U, multiuser_get_uid(10, 0));
+    EXPECT_EQ(1001000U, multiuser_get_uid(10, 1000));
+    EXPECT_EQ(1010000U, multiuser_get_uid(10, 10000));
+    EXPECT_EQ(1050000U, multiuser_get_uid(10, 50000));
+}
+
+TEST(MultiuserTest, TestSplitUser) {
+    EXPECT_EQ(0U, multiuser_get_user_id(0));
+    EXPECT_EQ(0U, multiuser_get_user_id(1000));
+    EXPECT_EQ(0U, multiuser_get_user_id(10000));
+    EXPECT_EQ(0U, multiuser_get_user_id(50000));
+    EXPECT_EQ(10U, multiuser_get_user_id(1000000));
+    EXPECT_EQ(10U, multiuser_get_user_id(1001000));
+    EXPECT_EQ(10U, multiuser_get_user_id(1010000));
+    EXPECT_EQ(10U, multiuser_get_user_id(1050000));
+}
+
+TEST(MultiuserTest, TestSplitApp) {
+    EXPECT_EQ(0U, multiuser_get_app_id(0));
+    EXPECT_EQ(1000U, multiuser_get_app_id(1000));
+    EXPECT_EQ(10000U, multiuser_get_app_id(10000));
+    EXPECT_EQ(50000U, multiuser_get_app_id(50000));
+    EXPECT_EQ(0U, multiuser_get_app_id(1000000));
+    EXPECT_EQ(1000U, multiuser_get_app_id(1001000));
+    EXPECT_EQ(10000U, multiuser_get_app_id(1010000));
+    EXPECT_EQ(50000U, multiuser_get_app_id(1050000));
+}
+
+TEST(MultiuserTest, TestCache) {
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 0));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 1000));
+    EXPECT_EQ(20000U, multiuser_get_cache_gid(0, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 50000));
+    EXPECT_EQ(1020000U, multiuser_get_cache_gid(10, 10000));
+}
+
+TEST(MultiuserTest, TestExt) {
+    EXPECT_EQ(ERR_GID, multiuser_get_ext_gid(0, 0));
+    EXPECT_EQ(ERR_GID, multiuser_get_ext_gid(0, 1000));
+    EXPECT_EQ(30000U, multiuser_get_ext_gid(0, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_ext_gid(0, 50000));
+    EXPECT_EQ(1030000U, multiuser_get_ext_gid(10, 10000));
+}
+
+TEST(MultiuserTest, TestShared) {
+    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 0));
+    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 1000));
+    EXPECT_EQ(50000U, multiuser_get_shared_gid(0, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 50000));
+    EXPECT_EQ(1050000U, multiuser_get_shared_gid(10, 10000));
+}
diff --git a/libcutils/tests/sockets_test.cpp b/libcutils/tests/sockets_test.cpp
index 0441fb6..b762ac1 100644
--- a/libcutils/tests/sockets_test.cpp
+++ b/libcutils/tests/sockets_test.cpp
@@ -101,7 +101,7 @@
     // should always be able to read its port.
     for (int port : {10000, 12345, 15999, 20202, 25000}) {
         for (int type : {SOCK_DGRAM, SOCK_STREAM}) {
-            server = socket_inaddr_any_server(port, SOCK_DGRAM);
+            server = socket_inaddr_any_server(port, type);
             if (server != INVALID_SOCKET) {
                 EXPECT_EQ(port, socket_get_local_port(server));
             }
diff --git a/libcutils/uevent.c b/libcutils/uevent.c
index de5d227..f548dca 100644
--- a/libcutils/uevent.c
+++ b/libcutils/uevent.c
@@ -116,7 +116,12 @@
     if(s < 0)
         return -1;
 
-    setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz));
+    /* buf_sz should be less than net.core.rmem_max for this to succeed */
+    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0) {
+        close(s);
+        return -1;
+    }
+
     setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
 
     if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
diff --git a/libdiskconfig/config_mbr.c b/libdiskconfig/config_mbr.c
index 1d3cd20..ace9bbf 100644
--- a/libdiskconfig/config_mbr.c
+++ b/libdiskconfig/config_mbr.c
@@ -22,8 +22,8 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <android/log.h>
 #include <diskconfig/diskconfig.h>
+#include <log/log.h>
 
 /* start and len are in LBA units */
 static void
diff --git a/libdiskconfig/diskconfig.c b/libdiskconfig/diskconfig.c
index 2d59ad9..c7e1b43 100644
--- a/libdiskconfig/diskconfig.c
+++ b/libdiskconfig/diskconfig.c
@@ -28,8 +28,8 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include <android/log.h>
 #include <cutils/config_utils.h>
+#include <log/log.h>
 
 #include <diskconfig/diskconfig.h>
 
diff --git a/libdiskconfig/diskutils.c b/libdiskconfig/diskutils.c
index 3a27601..fe1b4c1 100644
--- a/libdiskconfig/diskutils.c
+++ b/libdiskconfig/diskutils.c
@@ -26,7 +26,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include <android/log.h>
+#include <log/log.h>
 
 #include <diskconfig/diskconfig.h>
 
diff --git a/libdiskconfig/dump_diskconfig.c b/libdiskconfig/dump_diskconfig.c
index c94e7f4..3c4f620 100644
--- a/libdiskconfig/dump_diskconfig.c
+++ b/libdiskconfig/dump_diskconfig.c
@@ -19,7 +19,7 @@
 
 #include <stdio.h>
 
-#include <android/log.h>
+#include <log/log.h>
 
 #include "diskconfig.h"
 
diff --git a/libdiskconfig/write_lst.c b/libdiskconfig/write_lst.c
index 21d4a31..c3d5c0a 100644
--- a/libdiskconfig/write_lst.c
+++ b/libdiskconfig/write_lst.c
@@ -23,8 +23,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <android/log.h>
 #include <diskconfig/diskconfig.h>
+#include <log/log.h>
 
 struct write_list *
 alloc_wl(uint32_t data_len)
diff --git a/libion/ion.c b/libion/ion.c
index a7b22b8..9aaa6f2 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -29,8 +29,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <android/log.h>
 #include <ion/ion.h>
+#include <log/log.h>
 
 int ion_open()
 {
diff --git a/liblog/Android.bp b/liblog/Android.bp
index e59a460..310dbf4 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -15,12 +15,17 @@
 //
 
 liblog_sources = [
+    "config_read.c",
+    "config_write.c",
+    "local_logger.c",
     "log_event_list.c",
     "log_event_write.c",
-    "logger_write.c",
-    "config_write.c",
-    "logger_name.c",
+    "log_ratelimit.cpp",
     "logger_lock.c",
+    "logger_name.c",
+    "logger_read.c",
+    "logger_write.c",
+    "logprint.c",
 ]
 liblog_host_sources = [
     "fake_log_device.c",
@@ -28,15 +33,12 @@
 ]
 liblog_target_sources = [
     "event_tag_map.cpp",
-    "config_read.c",
     "log_time.cpp",
-    "log_is_loggable.c",
-    "logprint.c",
+    "properties.c",
     "pmsg_reader.c",
     "pmsg_writer.c",
     "logd_reader.c",
     "logd_writer.c",
-    "logger_read.c",
 ]
 
 // Shared and static library for host and device
@@ -56,7 +58,7 @@
             srcs: liblog_target_sources,
             // AddressSanitizer runtime library depends on liblog.
             sanitize: {
-                never: true,
+                address: false,
             },
         },
         android_arm: {
@@ -78,6 +80,8 @@
         },
     },
 
+    export_include_dirs: ["include"],
+
     cflags: [
         "-Werror",
         "-fvisibility=hidden",
@@ -94,24 +98,22 @@
     compile_multilib: "both",
 }
 
-// system/core/android/log.h needs some work before it can be included in the
-// NDK. It defines a *lot* of macros that previously were usable names in NDK
-// sources that used android/log.h. As an example, the following file defines
-// LOG_TAG as a variable, but the variable name gets macro replaced if we use
-// the current android/log.h.
-// https://android.googlesource.com/platform/external/deqp/+/4adc1515f867b26c19c2f7498e9de93a230a234d/framework/platform/android/tcuTestLogParserJNI.cpp#41
-//
-// For now, we keep a copy of the old NDK android/log.h in legacy-ndk-includes.
 ndk_headers {
     name: "liblog_headers",
-    from: "legacy-ndk-includes",
+    from: "include/android",
     to: "android",
-    srcs: ["legacy-ndk-includes/log.h"],
+    srcs: ["include/android/log.h"],
     license: "NOTICE",
 }
 
+cc_library_headers {
+    name: "liblog_vndk_headers",
+    export_include_dirs: ["include_vndk"],
+}
+
 ndk_library {
     name: "liblog.ndk",
     symbol_file: "liblog.map.txt",
     first_version: "9",
+    unversioned_until: "current",
 }
diff --git a/liblog/README b/liblog/README
index 610338c..40a39ad 100644
--- a/liblog/README
+++ b/liblog/README
@@ -108,6 +108,11 @@
 
        int android_log_destroy(android_log_context *ctx)
 
+       #include <log/log_frontend.h>
+
+       int android_set_log_frontend(int frontend_flag)
+       int android_get_log_frontend()
+
        Link with -llog
 
 DESCRIPTION
@@ -162,6 +167,13 @@
        when  opening  the  sub-log.    It  is  recommended  to  open  the  log
        ANDROID_LOG_RDONLY in these cases.
 
+       android_set_log_frontend() selects frontend filters. Argument is either
+       LOGGER_DEFAULT, LOGGER_LOGD, LOGGER_NULL or LOGGER_LOCAL. Log to logger
+       daemon for default or logd,  drop contents on floor,  or log into local
+       memory   respectively.          Both   android_set_log_frontend()   and
+       android_get_log_frontend()  return  the  current  frontend mask,   or a
+       negative errno for any problems.
+
 ERRORS
        If messages fail, a negative error code will be returned to the caller.
 
@@ -194,4 +206,4 @@
 
 
 
-                                  17 Oct 2016                        LIBLOG(3)
+                                  08 Feb 2017                        LIBLOG(3)
diff --git a/liblog/config_read.c b/liblog/config_read.c
index 1f54152..b9a281b 100644
--- a/liblog/config_read.c
+++ b/liblog/config_read.c
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <log/log_frontend.h>
+
 #include "config_read.h"
 #include "logger.h"
 
@@ -52,11 +54,35 @@
 }
 
 LIBLOG_HIDDEN void __android_log_config_read() {
-#if (FAKE_LOG_DEVICE == 0)
-    extern struct android_log_transport_read logdLoggerRead;
-    extern struct android_log_transport_read pmsgLoggerRead;
+    if (__android_log_frontend & LOGGER_LOCAL) {
+        extern struct android_log_transport_read localLoggerRead;
 
-    __android_log_add_transport(&__android_log_transport_read, &logdLoggerRead);
-    __android_log_add_transport(&__android_log_persist_read, &pmsgLoggerRead);
+        __android_log_add_transport(&__android_log_transport_read,
+                                    &localLoggerRead);
+    }
+
+#if (FAKE_LOG_DEVICE == 0)
+    if ((__android_log_frontend == LOGGER_DEFAULT) ||
+        (__android_log_frontend & LOGGER_LOGD)) {
+        extern struct android_log_transport_read logdLoggerRead;
+        extern struct android_log_transport_read pmsgLoggerRead;
+
+        __android_log_add_transport(&__android_log_transport_read,
+                                    &logdLoggerRead);
+        __android_log_add_transport(&__android_log_persist_read,
+                                    &pmsgLoggerRead);
+    }
 #endif
 }
+
+LIBLOG_HIDDEN void __android_log_config_read_close() {
+    struct android_log_transport_read *transport;
+    struct listnode *n;
+
+    read_transport_for_each_safe(transport, n, &__android_log_transport_read) {
+        list_remove(&transport->node);
+    }
+    read_transport_for_each_safe(transport, n, &__android_log_persist_read) {
+        list_remove(&transport->node);
+    }
+}
diff --git a/liblog/config_read.h b/liblog/config_read.h
index 49a3b75..892e80d 100644
--- a/liblog/config_read.h
+++ b/liblog/config_read.h
@@ -28,22 +28,31 @@
 
 #define read_transport_for_each(transp, transports)                         \
     for ((transp) = node_to_item((transports)->next,                        \
-                               struct android_log_transport_read, node);    \
-         ((transp) != node_to_item(transports,                              \
-                                 struct android_log_transport_read, node)); \
+                                 struct android_log_transport_read, node);  \
+         ((transp) != node_to_item((transports),                            \
+                                   struct android_log_transport_read,       \
+                                   node)) &&                                \
+         ((transp) != node_to_item((transp)->node.next,                     \
+                                   struct android_log_transport_read,       \
+                                   node));                                  \
          (transp) = node_to_item((transp)->node.next,                       \
-                               struct android_log_transport_read, node))    \
+                                 struct android_log_transport_read, node))
 
 #define read_transport_for_each_safe(transp, n, transports)                 \
     for ((transp) = node_to_item((transports)->next,                        \
-                               struct android_log_transport_read, node),    \
+                                 struct android_log_transport_read, node),  \
          (n) = (transp)->node.next;                                         \
-         ((transp) != node_to_item(transports,                              \
-                                 struct android_log_transport_read, node)); \
-         (transp) = node_to_item(n, struct android_log_transport_read, node), \
+         ((transp) != node_to_item((transports),                            \
+                                   struct android_log_transport_read,       \
+                                   node)) &&                                \
+         ((transp) != node_to_item((n), struct android_log_transport_read,  \
+                                   node));                                  \
+         (transp) = node_to_item((n), struct android_log_transport_read,    \
+                                 node),                                     \
          (n) = (transp)->node.next)
 
 LIBLOG_HIDDEN void __android_log_config_read();
+LIBLOG_HIDDEN void __android_log_config_read_close();
 
 __END_DECLS
 
diff --git a/liblog/config_write.c b/liblog/config_write.c
index d689f63..583dcff 100644
--- a/liblog/config_write.c
+++ b/liblog/config_write.c
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <log/log_frontend.h>
+
 #include "config_write.h"
 #include "logger.h"
 
@@ -52,15 +54,42 @@
 }
 
 LIBLOG_HIDDEN void __android_log_config_write() {
+    if (__android_log_frontend & LOGGER_LOCAL) {
+        extern struct android_log_transport_write localLoggerWrite;
+
+        __android_log_add_transport(&__android_log_transport_write,
+                                    &localLoggerWrite);
+    }
+
+    if ((__android_log_frontend == LOGGER_DEFAULT) ||
+        (__android_log_frontend & LOGGER_LOGD)) {
 #if (FAKE_LOG_DEVICE == 0)
-    extern struct android_log_transport_write logdLoggerWrite;
-    extern struct android_log_transport_write pmsgLoggerWrite;
+        extern struct android_log_transport_write logdLoggerWrite;
+        extern struct android_log_transport_write pmsgLoggerWrite;
 
-    __android_log_add_transport(&__android_log_transport_write, &logdLoggerWrite);
-    __android_log_add_transport(&__android_log_persist_write, &pmsgLoggerWrite);
+        __android_log_add_transport(&__android_log_transport_write,
+                                    &logdLoggerWrite);
+        __android_log_add_transport(&__android_log_persist_write,
+                                    &pmsgLoggerWrite);
 #else
-    extern struct android_log_transport_write fakeLoggerWrite;
+        extern struct android_log_transport_write fakeLoggerWrite;
 
-    __android_log_add_transport(&__android_log_transport_write, &fakeLoggerWrite);
+        __android_log_add_transport(&__android_log_transport_write,
+                                    &fakeLoggerWrite);
 #endif
+    }
+}
+
+LIBLOG_HIDDEN void __android_log_config_write_close() {
+    struct android_log_transport_write *transport;
+    struct listnode *n;
+
+    write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
+        transport->logMask = 0;
+        list_remove(&transport->node);
+    }
+    write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
+        transport->logMask = 0;
+        list_remove(&transport->node);
+    }
 }
diff --git a/liblog/config_write.h b/liblog/config_write.h
index 3b01a9a..8825411 100644
--- a/liblog/config_write.h
+++ b/liblog/config_write.h
@@ -29,21 +29,30 @@
 #define write_transport_for_each(transp, transports)                         \
     for ((transp) = node_to_item((transports)->next,                         \
                                  struct android_log_transport_write, node);  \
-         ((transp) != node_to_item(transports,                               \
-                                 struct android_log_transport_write, node)); \
+         ((transp) != node_to_item((transports),                             \
+                                   struct android_log_transport_write,       \
+                                   node)) &&                                 \
+         ((transp) != node_to_item((transp)->node.next,                      \
+                                   struct android_log_transport_write,       \
+                                   node));                                   \
          (transp) = node_to_item((transp)->node.next,                        \
-                                 struct android_log_transport_write, node))  \
+                                 struct android_log_transport_write, node))
 
 #define write_transport_for_each_safe(transp, n, transports)                 \
     for ((transp) = node_to_item((transports)->next,                         \
                                  struct android_log_transport_write, node),  \
          (n) = (transp)->node.next;                                          \
-         ((transp) != node_to_item(transports,                               \
-                                   struct android_log_transport_write, node)); \
-         (transp) = node_to_item(n, struct android_log_transport_write, node), \
+         ((transp) != node_to_item((transports),                             \
+                                   struct android_log_transport_write,       \
+                                   node)) &&                                 \
+         ((transp) != node_to_item((n), struct android_log_transport_write,  \
+                                   node));                                   \
+         (transp) = node_to_item((n), struct android_log_transport_write,    \
+                                 node),                                      \
          (n) = (transp)->node.next)
 
 LIBLOG_HIDDEN void __android_log_config_write();
+LIBLOG_HIDDEN void __android_log_config_write_close();
 
 __END_DECLS
 
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index e8e0335..42f0f37 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2007-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.
@@ -19,367 +19,238 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
 
-#include <android/log.h>
+#include <experimental/string_view>
+#include <functional>
+#include <string>
+#include <unordered_map>
+
 #include <log/event_tag_map.h>
+#include <utils/FastStrcmp.h>
+#include <utils/RWLock.h>
+#include <private/android_logger.h>
 
 #include "log_portability.h"
+#include "logd_reader.h"
 
 #define OUT_TAG "EventTagMap"
 
-/*
- * Single entry.
- */
-typedef struct EventTag {
-    uint32_t tagIndex;
-    char*    tagStr;
-    size_t   tagLen;
-    char*    fmtStr;
-    size_t   fmtLen;
-} EventTag;
+class MapString {
+private:
+    const std::string* alloc; // HAS-AN
+    const std::experimental::string_view str; // HAS-A
 
-/*
- * Map.
- */
-struct EventTagMap {
-    /* memory-mapped source file; we get strings from here */
-    void*           mapAddr;
-    size_t          mapLen;
+public:
+    operator const std::experimental::string_view() const { return str; }
 
-    /* array of event tags, sorted numerically by tag index */
-    EventTag*       tagArray;
-    int             numTags;
+    const char* data() const { return str.data(); }
+    size_t length() const { return str.length(); }
+
+    bool operator== (const MapString& rval) const {
+        if (length() != rval.length()) return false;
+        if (length() == 0) return true;
+        return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
+    }
+    bool operator!= (const MapString& rval) const {
+        return !(*this == rval);
+    }
+
+    MapString(const char* str, size_t len) : alloc(NULL), str(str, len) { }
+    explicit MapString(const std::string& str) :
+            alloc(new std::string(str)),
+            str(alloc->data(), alloc->length()) { }
+    MapString(MapString &&rval) :
+            alloc(rval.alloc),
+            str(rval.data(), rval.length()) {
+        rval.alloc = NULL;
+    }
+    explicit MapString(const MapString &rval) :
+            alloc(rval.alloc ? new std::string(*rval.alloc) : NULL),
+            str(alloc ? alloc->data() : rval.data(), rval.length()) { }
+
+    ~MapString() { if (alloc) delete alloc; }
 };
 
-/* fwd */
-static int processFile(EventTagMap* map);
-static int countMapLines(const EventTagMap* map);
-static int parseMapLines(EventTagMap* map);
-static int scanTagLine(char** pData, EventTag* tag, int lineNum);
-static int sortTags(EventTagMap* map);
+// Hash for MapString
+template <> struct std::hash<MapString>
+        : public std::unary_function<const MapString&, size_t> {
+    size_t operator()(const MapString& __t) const noexcept {
+        if (!__t.length()) return 0;
+        return std::hash<std::experimental::string_view>()(std::experimental::string_view(__t));
+    }
+};
 
-/*
- * Open the map file and allocate a structure to manage it.
- *
- * We create a private mapping because we want to terminate the log tag
- * strings with '\0'.
- */
-LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName)
-{
-    EventTagMap* newTagMap;
-    off_t end;
-    int save_errno;
-    const char* tagfile = fileName ? fileName : EVENT_TAG_MAP_FILE;
+typedef std::pair<MapString, MapString> TagFmt;
 
-    int fd = open(tagfile, O_RDONLY | O_CLOEXEC);
-    if (fd < 0) {
-        save_errno = errno;
-        fprintf(stderr, "%s: unable to open map '%s': %s\n",
-                OUT_TAG, tagfile, strerror(save_errno));
-        goto fail_errno;
+template <> struct std::hash<TagFmt>
+        : public std::unary_function<const TagFmt&, size_t> {
+    size_t operator()(const TagFmt& __t) const noexcept {
+        // Tag is typically unique.  Will cost us an extra 100ns for the
+        // unordered_map lookup if we instead did a hash that combined
+        // both of tag and fmt members, e.g.:
+        //
+        // return std::hash<MapString>()(__t.first) ^
+        //        std::hash<MapString>()(__t.second);
+        return std::hash<MapString>()(__t.first);
+    }
+};
+
+// Map
+struct EventTagMap {
+#   define NUM_MAPS 2
+    // memory-mapped source file; we get strings from here
+    void*  mapAddr[NUM_MAPS];
+    size_t mapLen[NUM_MAPS];
+
+private:
+    std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;
+    std::unordered_map<TagFmt, uint32_t> TagFmt2Idx;
+    std::unordered_map<MapString, uint32_t> Tag2Idx;
+    // protect unordered sets
+    android::RWLock rwlock;
+
+public:
+    EventTagMap() {
+        memset(mapAddr, 0, sizeof(mapAddr));
+        memset(mapLen, 0, sizeof(mapLen));
     }
 
-    end = lseek(fd, 0L, SEEK_END);
-    save_errno = errno;
-    (void) lseek(fd, 0L, SEEK_SET);
-    if (end < 0) {
-        fprintf(stderr, "%s: unable to seek map '%s' %s\n",
-                OUT_TAG, tagfile, strerror(save_errno));
-        goto fail_close;
-    }
-
-    newTagMap = (EventTagMap*)calloc(1, sizeof(EventTagMap));
-    if (newTagMap == NULL) {
-        save_errno = errno;
-        goto fail_close;
-    }
-
-    newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
-    save_errno = errno;
-    close(fd);
-    fd = -1;
-    if ((newTagMap->mapAddr == MAP_FAILED) || (newTagMap->mapAddr == NULL)) {
-        fprintf(stderr, "%s: mmap(%s) failed: %s\n",
-                OUT_TAG, tagfile, strerror(save_errno));
-        goto fail_free;
-    }
-
-    newTagMap->mapLen = end;
-
-    if (processFile(newTagMap) != 0) goto fail_unmap;
-
-    return newTagMap;
-
-fail_unmap:
-    munmap(newTagMap->mapAddr, newTagMap->mapLen);
-    save_errno = EINVAL;
-fail_free:
-    free(newTagMap);
-fail_close:
-    close(fd);
-fail_errno:
-    errno = save_errno;
-fail:
-    return NULL;
-}
-
-/*
- * Close the map.
- */
-LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map)
-{
-    if (map == NULL) return;
-
-    munmap(map->mapAddr, map->mapLen);
-    free(map->tagArray);
-    free(map);
-}
-
-/*
- * Look up an entry in the map.
- *
- * The entries are sorted by tag number, so we can do a binary search.
- */
-LIBLOG_ABI_PUBLIC const char* android_lookupEventTag_len(const EventTagMap* map,
-                                                         size_t *len,
-                                                         unsigned int tag)
-{
-    int lo = 0;
-    int hi = map->numTags - 1;
-
-    while (lo <= hi) {
-        int mid = (lo + hi) / 2;
-        int cmp = map->tagArray[mid].tagIndex - tag;
-
-        if (cmp < 0) {
-            /* tag is bigger */
-            lo = mid + 1;
-        } else if (cmp > 0) {
-            /* tag is smaller */
-            hi = mid - 1;
-        } else {
-            /* found */
-            if (len) *len = map->tagArray[mid].tagLen;
-            /*
-             * b/31456426 to check if gTest can detect copy-on-write issue
-             * add the following line to break us:
-             *     map->tagArray[mid].tagStr[map->tagArray[mid].tagLen] = '\0';
-             * or explicitly use deprecated android_lookupEventTag().
-             */
-            return map->tagArray[mid].tagStr;
-        }
-    }
-
-    errno = ENOENT;
-    if (len) *len = 0;
-    return NULL;
-}
-
-/*
- * Look up an entry in the map.
- *
- * The entries are sorted by tag number, so we can do a binary search.
- */
-LIBLOG_ABI_PUBLIC const char* android_lookupEventFormat_len(
-    const EventTagMap* map, size_t *len, unsigned int tag)
-{
-    int lo = 0;
-    int hi = map->numTags - 1;
-
-    while (lo <= hi) {
-        int mid = (lo + hi) / 2;
-        int cmp = map->tagArray[mid].tagIndex - tag;
-
-        if (cmp < 0) {
-            /* tag is bigger */
-            lo = mid + 1;
-        } else if (cmp > 0) {
-            /* tag is smaller */
-            hi = mid - 1;
-        } else {
-            /* found */
-            if (len) *len = map->tagArray[mid].fmtLen;
-            return map->tagArray[mid].fmtStr;
-        }
-    }
-
-    errno = ENOENT;
-    if (len) *len = 0;
-    return NULL;
-}
-
-LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
-                                                     unsigned int tag)
-{
-    size_t len;
-    const char* tagStr = android_lookupEventTag_len(map, &len, tag);
-    char* cp;
-
-    if (!tagStr) return tagStr;
-    cp = (char*)tagStr;
-    cp += len;
-    if (*cp) *cp = '\0'; /* Trigger copy on write :-( */
-    return tagStr;
-}
-
-/*
- * Crunch through the file, parsing the contents and creating a tag index.
- */
-static int processFile(EventTagMap* map)
-{
-    /* get a tag count */
-    map->numTags = countMapLines(map);
-    if (map->numTags < 0) {
-        errno = ENOENT;
-        return -1;
-    }
-
-    /* allocate storage for the tag index array */
-    map->tagArray = (EventTag*)calloc(1, sizeof(EventTag) * map->numTags);
-    if (map->tagArray == NULL) return -1;
-
-    /* parse the file, null-terminating tag strings */
-    if (parseMapLines(map) != 0) return -1;
-
-    /* sort the tags and check for duplicates */
-    if (sortTags(map) != 0) return -1;
-
-    return 0;
-}
-
-/*
- * Run through all lines in the file, determining whether they're blank,
- * comments, or possibly have a tag entry.
- *
- * This is a very "loose" scan.  We don't try to detect syntax errors here.
- * The later pass is more careful, but the number of tags found there must
- * match the number of tags found here.
- *
- * Returns the number of potential tag entries found.
- */
-static int countMapLines(const EventTagMap* map)
-{
-    const char* cp = (const char*) map->mapAddr;
-    const char* endp = cp + map->mapLen;
-    int numTags = 0;
-    int unknown = 1;
-
-    while (cp < endp) {
-        if (*cp == '\n') {
-            unknown = 1;
-        } else if (unknown) {
-            if (isdigit(*cp)) {
-                /* looks like a tag to me */
-                numTags++;
-                unknown = 0;
-            } else if (isspace(*cp)) {
-                /* might be leading whitespace before tag num, keep going */
-            } else {
-                /* assume comment; second pass can complain in detail */
-                unknown = 0;
+    ~EventTagMap() {
+        Idx2TagFmt.clear();
+        TagFmt2Idx.clear();
+        Tag2Idx.clear();
+        for (size_t which = 0; which < NUM_MAPS; ++which) {
+            if (mapAddr[which]) {
+                munmap(mapAddr[which], mapLen[which]);
+                mapAddr[which] = 0;
             }
-        } else {
-            /* we've made up our mind; just scan to end of line */
         }
-        cp++;
     }
 
-    return numTags;
-}
+    bool emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose = false);
+    const TagFmt* find(uint32_t tag) const;
+    int find(TagFmt&& tagfmt) const;
+    int find(MapString&& tag) const;
+};
 
-/*
- * Parse the tags out of the file.
- */
-static int parseMapLines(EventTagMap* map)
-{
-    int tagNum, lineStart, lineNum;
-    char* cp = (char*) map->mapAddr;
-    char* endp = cp + map->mapLen;
-
-    /* insist on EOL at EOF; simplifies parsing and null-termination */
-    if (*(endp - 1) != '\n') {
-        fprintf(stderr, "%s: map file missing EOL on last line\n", OUT_TAG);
-        errno = EINVAL;
-        return -1;
-    }
-
-    tagNum = 0;
-    lineStart = 1;
-    lineNum = 1;
-    while (cp < endp) {
-        if (*cp == '\n') {
-            lineStart = 1;
-            lineNum++;
-        } else if (lineStart) {
-            if (*cp == '#') {
-                /* comment; just scan to end */
-                lineStart = 0;
-            } else if (isdigit(*cp)) {
-                /* looks like a tag; scan it out */
-                if (tagNum >= map->numTags) {
-                    fprintf(stderr,
-                        "%s: more tags than expected (%d)\n", OUT_TAG, tagNum);
-                    errno = EMFILE;
-                    return -1;
-                }
-                if (scanTagLine(&cp, &map->tagArray[tagNum], lineNum) != 0) {
-                    return -1;
-                }
-                tagNum++;
-                lineNum++;      // we eat the '\n'
-                /* leave lineStart==1 */
-            } else if (isspace(*cp)) {
-                /* looks like leading whitespace; keep scanning */
-            } else {
-                fprintf(stderr,
-                    "%s: unexpected chars (0x%02x) in tag number on line %d\n",
-                    OUT_TAG, *cp, lineNum);
-                errno = EINVAL;
-                return -1;
+bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose) {
+    bool ret = true;
+    static const char errorFormat[] = OUT_TAG ": duplicate tag entries %" PRIu32
+                                      ":%.*s:%.*s and %" PRIu32
+                                      ":%.*s:%.*s)\n";
+    android::RWLock::AutoWLock writeLock(rwlock);
+    {
+        std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+        it = Idx2TagFmt.find(tag);
+        if (it != Idx2TagFmt.end()) {
+            if (verbose) {
+                fprintf(stderr, errorFormat,
+                        it->first,
+                        (int)it->second.first.length(), it->second.first.data(),
+                        (int)it->second.second.length(), it->second.second.data(),
+                        tag,
+                        (int)tagfmt.first.length(), tagfmt.first.data(),
+                        (int)tagfmt.second.length(), tagfmt.second.data());
             }
+            ret = false;
         } else {
-            /* this is a blank or comment line */
+            Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
         }
-        cp++;
     }
 
-    if (tagNum != map->numTags) {
-        fprintf(stderr, "%s: parsed %d tags, expected %d\n",
-            OUT_TAG, tagNum, map->numTags);
-        errno = EINVAL;
-        return -1;
+    {
+        std::unordered_map<TagFmt, uint32_t>::const_iterator it;
+        it = TagFmt2Idx.find(tagfmt);
+        if (it != TagFmt2Idx.end()) {
+            if (verbose) {
+                fprintf(stderr, errorFormat,
+                        it->second,
+                        (int)it->first.first.length(), it->first.first.data(),
+                        (int)it->first.second.length(), it->first.second.data(),
+                        tag,
+                        (int)tagfmt.first.length(), tagfmt.first.data(),
+                        (int)tagfmt.second.length(), tagfmt.second.data());
+            }
+            ret = false;
+        } else {
+            TagFmt2Idx.emplace(std::make_pair(tagfmt, tag));
+        }
     }
 
-    return 0;
+    {
+        std::unordered_map<MapString, uint32_t>::const_iterator it;
+        it = Tag2Idx.find(tagfmt.first);
+        if (!tagfmt.second.length() && (it != Tag2Idx.end())) {
+            Tag2Idx.erase(it);
+            it = Tag2Idx.end();
+        }
+        if (it == Tag2Idx.end()) {
+            Tag2Idx.emplace(std::make_pair(tagfmt.first, tag));
+        }
+    }
+
+    return ret;
 }
 
-/*
- * Scan one tag line.
- *
- * "*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).
- *
- * Returns 0 on success, nonzero on failure.
- */
-static int scanTagLine(char** pData, EventTag* tag, int lineNum)
-{
-    char* cp;
+const TagFmt* EventTagMap::find(uint32_t tag) const {
+    std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+    android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+    it = Idx2TagFmt.find(tag);
+    if (it == Idx2TagFmt.end()) return NULL;
+    return &(it->second);
+}
 
+int EventTagMap::find(TagFmt&& tagfmt) const {
+    std::unordered_map<TagFmt, uint32_t>::const_iterator it;
+    android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+    it = TagFmt2Idx.find(std::move(tagfmt));
+    if (it == TagFmt2Idx.end()) return -1;
+    return it->second;
+}
+
+int EventTagMap::find(MapString&& tag) const {
+    std::unordered_map<MapString, uint32_t>::const_iterator it;
+    android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+    it = Tag2Idx.find(std::move(tag));
+    if (it == Tag2Idx.end()) return -1;
+    return it->second;
+}
+
+// Scan one tag line.
+//
+// "*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).
+//
+// lineNum = 0 removes verbose comments and requires us to cache the
+// content rather than make direct raw references since the content
+// will disappear after the call. A non-zero lineNum means we own the
+// 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) {
-        fprintf(stderr, "%s: malformed tag number on line %d\n", OUT_TAG, lineNum);
+        if (lineNum) {
+            fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n",
+                    lineNum);
+        }
         errno = EINVAL;
         return -1;
     }
 
-    tag->tagIndex = val;
-    if (tag->tagIndex != val) {
-        fprintf(stderr, "%s: tag number too large on line %d\n", OUT_TAG, lineNum);
+    uint32_t tagIndex = val;
+    if (tagIndex != val) {
+        if (lineNum) {
+            fprintf(stderr, OUT_TAG ": tag number too large on line %d\n",
+                    lineNum);
+        }
         errno = ERANGE;
         return -1;
     }
@@ -388,76 +259,379 @@
     }
 
     if (*cp == '\n') {
-        fprintf(stderr, "%s: missing tag string on line %d\n", OUT_TAG, lineNum);
+        if (lineNum) {
+            fprintf(stderr, OUT_TAG ": missing tag string on line %d\n",
+                    lineNum);
+        }
         errno = EINVAL;
         return -1;
     }
 
-    tag->tagStr = cp;
-
-    /* Determine whether "c" is a valid tag char. */
-    while (isalnum(*++cp) || (*cp == '_')) {
-    }
-    tag->tagLen = cp - tag->tagStr;
+    const char* tag = cp;
+    // Determine whether "c" is a valid tag char.
+    while (isalnum(*++cp) || (*cp == '_')) { }
+    size_t tagLen = cp - tag;
 
     if (!isspace(*cp)) {
-        fprintf(stderr, "%s: invalid tag chars on line %d\n", OUT_TAG, lineNum);
+        if (lineNum) {
+            fprintf(stderr, OUT_TAG ": invalid tag chars on line %d\n",
+                    lineNum);
+        }
         errno = EINVAL;
         return -1;
     }
 
     while (isspace(*cp) && (*cp != '\n')) ++cp;
+    const char* fmt = NULL;
+    size_t fmtLen = 0;
     if (*cp != '#') {
-        tag->fmtStr = cp;
+        fmt = cp;
         while ((*cp != '\n') && (*cp != '#')) ++cp;
-        while ((cp > tag->fmtStr) && isspace(*(cp - 1))) --cp;
-        tag->fmtLen = cp - tag->fmtStr;
+        while ((cp > fmt) && isspace(*(cp - 1))) --cp;
+        fmtLen = cp - fmt;
+    }
+
+    // KISS Only report identicals if they are global
+    // Ideally we want to check if there are identicals
+    // recorded for the same uid, but recording that
+    // unused detail in our database is too burdensome.
+    bool verbose = true;
+    while ((*cp != '#') && (*cp != '\n')) ++cp;
+    if (*cp == '#') {
+        do {
+            ++cp;
+        } while (isspace(*cp) && (*cp != '\n'));
+        verbose = !!fastcmp<strncmp>(cp, "uid=", strlen("uid="));
     }
 
     while (*cp != '\n') ++cp;
+#ifdef DEBUG
+    fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - *pData), *pData);
+#endif
     *pData = cp;
 
-    return 0;
-}
-
-/*
- * Compare two EventTags.
- */
-static int compareEventTags(const void* v1, const void* v2)
-{
-    const EventTag* tag1 = (const EventTag*) v1;
-    const EventTag* tag2 = (const EventTag*) v2;
-
-    return tag1->tagIndex - tag2->tagIndex;
-}
-
-/*
- * Sort the EventTag array so we can do fast lookups by tag index.  After
- * the sort we do a quick check for duplicate tag indices.
- *
- * Returns 0 on success.
- */
-static int sortTags(EventTagMap* map)
-{
-    int i;
-
-    qsort(map->tagArray, map->numTags, sizeof(EventTag), compareEventTags);
-
-    for (i = 1; i < map->numTags; i++) {
-        if (map->tagArray[i].tagIndex == map->tagArray[i - 1].tagIndex) {
-            fprintf(stderr,
-                "%s: duplicate tag entries (%" PRIu32 ":%.*s:%.*s and %" PRIu32 ":%.*s:%.*s)\n",
-                OUT_TAG,
-                map->tagArray[i].tagIndex,
-                (int)map->tagArray[i].tagLen, map->tagArray[i].tagStr,
-                (int)map->tagArray[i].fmtLen, map->tagArray[i].fmtStr,
-                map->tagArray[i - 1].tagIndex,
-                (int)map->tagArray[i - 1].tagLen, map->tagArray[i - 1].fmtStr,
-                (int)map->tagArray[i - 1].fmtLen, map->tagArray[i - 1].fmtStr);
-            errno = EMLINK;
-            return -1;
+    if (lineNum) {
+        if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
+                MapString(tag, tagLen), MapString(fmt, fmtLen))), verbose)) {
+            return 0;
         }
+    } else {
+        // cache
+        if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
+                MapString(std::string(tag, tagLen)),
+                MapString(std::string(fmt, fmtLen)))))) {
+            return 0;
+        }
+    }
+    errno = EMLINK;
+    return -1;
+}
+
+static const char* eventTagFiles[NUM_MAPS] = {
+    EVENT_TAG_MAP_FILE,
+    "/dev/event-log-tags",
+};
+
+// Parse the tags out of the file.
+static int parseMapLines(EventTagMap* map, size_t which) {
+    char* cp = static_cast<char*>(map->mapAddr[which]);
+    size_t len = map->mapLen[which];
+    char* endp = cp + len;
+
+    // insist on EOL at EOF; simplifies parsing and null-termination
+    if (!len || (*(endp - 1) != '\n')) {
+#ifdef DEBUG
+        fprintf(stderr, OUT_TAG ": map file %zu[%zu] missing EOL on last line\n",
+                which, len);
+#endif
+        if (which) { // do not propagate errors for other files
+            return 0;
+        }
+        errno = EINVAL;
+        return -1;
+    }
+
+    bool lineStart = true;
+    int lineNum = 1;
+    while (cp < endp) {
+        if (*cp == '\n') {
+            lineStart = true;
+            lineNum++;
+        } else if (lineStart) {
+            if (*cp == '#') {
+                // comment; just scan to end
+                lineStart = false;
+            } else if (isdigit(*cp)) {
+                // looks like a tag; scan it out
+                if (scanTagLine(map, &cp, lineNum) != 0) {
+                    if (!which || (errno != EMLINK)) {
+                        return -1;
+                    }
+                }
+                lineNum++;      // we eat the '\n'
+                // leave lineStart==true
+            } else if (isspace(*cp)) {
+                // looks like leading whitespace; keep scanning
+            } else {
+                fprintf(stderr,
+                        OUT_TAG ": unexpected chars (0x%02x) in tag number on line %d\n",
+                        *cp, lineNum);
+                errno = EINVAL;
+                return -1;
+            }
+        } else {
+            // this is a blank or comment line
+        }
+        cp++;
     }
 
     return 0;
 }
+
+// Open the map file and allocate a structure to manage it.
+//
+// We create a private mapping because we want to terminate the log tag
+// strings with '\0'.
+LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName) {
+    EventTagMap* newTagMap;
+    off_t end[NUM_MAPS];
+    int save_errno, fd[NUM_MAPS];
+    size_t which;
+
+    memset(fd, -1, sizeof(fd));
+    memset(end, 0, sizeof(end));
+
+    for (which = 0; which < NUM_MAPS; ++which) {
+        const char* tagfile = fileName ? fileName : eventTagFiles[which];
+
+        fd[which] = open(tagfile, O_RDONLY | O_CLOEXEC);
+        if (fd[which] < 0) {
+            if (!which) {
+                save_errno = errno;
+                fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n",
+                        tagfile, strerror(save_errno));
+                goto fail_errno;
+            }
+            continue;
+        }
+        end[which] = lseek(fd[which], 0L, SEEK_END);
+        save_errno = errno;
+        (void)lseek(fd[which], 0L, SEEK_SET);
+        if (!which && (end[0] < 0)) {
+            fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n",
+                    tagfile, strerror(save_errno));
+            goto fail_close;
+        }
+        if (fileName) break; // Only allow one as specified
+    }
+
+    newTagMap = new EventTagMap;
+    if (newTagMap == NULL) {
+        save_errno = errno;
+        goto fail_close;
+    }
+
+    for (which = 0; which < NUM_MAPS; ++which) {
+        if (fd[which] >= 0) {
+            newTagMap->mapAddr[which] = 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]);
+            fd[which] = -1;
+            if ((newTagMap->mapAddr[which] != MAP_FAILED) &&
+                (newTagMap->mapAddr[which] != NULL)) {
+                newTagMap->mapLen[which] = end[which];
+            } else if (!which) {
+                const char* tagfile = fileName ? fileName : eventTagFiles[which];
+
+                fprintf(stderr, OUT_TAG ": mmap(%s) failed: %s\n",
+                        tagfile, strerror(save_errno));
+                goto fail_unmap;
+            }
+        }
+    }
+
+    for (which = 0; which < NUM_MAPS; ++which) {
+        if (parseMapLines(newTagMap, which) != 0) {
+            delete newTagMap;
+            return NULL;
+        }
+    }
+
+    return newTagMap;
+
+fail_unmap:
+    save_errno = EINVAL;
+    delete newTagMap;
+fail_close:
+    for (which = 0; which < NUM_MAPS; ++which) close(fd[which]);
+fail_errno:
+    errno = save_errno;
+    return NULL;
+}
+
+// Close the map.
+LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map) {
+    if (map) delete map;
+}
+
+// Cache miss, go to logd to acquire a public reference.
+// Because we lack access to a SHARED PUBLIC /dev/event-log-tags file map?
+static const TagFmt* __getEventTag(EventTagMap* map, unsigned int tag) {
+    // call event tag service to arrange for a new tag
+    char *buf = NULL;
+    // Can not use android::base::StringPrintf, asprintf + free instead.
+    static const char command_template[] = "getEventTag id=%u";
+    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;
+            } else {
+                size = ret;
+            }
+        } else {
+            size = ret;
+        }
+        // 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
+            if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
+                ++cp;
+                if (!scanTagLine(map, &cp, 0)) {
+                    free(buf);
+                    return map->find(tag);
+                }
+            }
+        }
+        free(buf);
+    }
+    return NULL;
+}
+
+// Look up an entry in the map.
+LIBLOG_ABI_PUBLIC const char* android_lookupEventTag_len(const EventTagMap* map,
+                                                         size_t *len,
+                                                         unsigned int tag) {
+    if (len) *len = 0;
+    const TagFmt* str = map->find(tag);
+    if (!str) {
+        str = __getEventTag(const_cast<EventTagMap*>(map), tag);
+    }
+    if (!str) return NULL;
+    if (len) *len = str->first.length();
+    return str->first.data();
+}
+
+// Look up an entry in the map.
+LIBLOG_ABI_PUBLIC const char* android_lookupEventFormat_len(
+        const EventTagMap* map, size_t *len, unsigned int tag) {
+    if (len) *len = 0;
+    const TagFmt* str = map->find(tag);
+    if (!str) {
+        str = __getEventTag(const_cast<EventTagMap*>(map), tag);
+    }
+    if (!str) return NULL;
+    if (len) *len = str->second.length();
+    return str->second.data();
+}
+
+// This function is deprecated and replaced with android_lookupEventTag_len
+// since it will cause the map to change from Shared and backed by a file,
+// to Private Dirty and backed up by swap, albeit highly compressible. By
+// deprecating this function everywhere, we save 100s of MB of memory space.
+LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
+                                                     unsigned int tag) {
+    size_t len;
+    const char* tagStr = android_lookupEventTag_len(map, &len, tag);
+
+    if (!tagStr) return tagStr;
+    char* cp = const_cast<char*>(tagStr);
+    cp += len;
+    if (*cp) *cp = '\0'; // Trigger copy on write :-( and why deprecated.
+    return tagStr;
+}
+
+// Look up tagname, generate one if necessary, and return a tag
+LIBLOG_ABI_PUBLIC int android_lookupEventTagNum(EventTagMap* map,
+                                                const char* tagname,
+                                                const char* format,
+                                                int prio) {
+    size_t len = strlen(tagname);
+    if (!len) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    if ((prio != ANDROID_LOG_UNKNOWN) && (prio < ANDROID_LOG_SILENT) &&
+            !__android_log_is_loggable_len(prio, tagname, len,
+                                           __android_log_is_debuggable() ?
+                                             ANDROID_LOG_VERBOSE :
+                                             ANDROID_LOG_DEBUG)) {
+        errno = EPERM;
+        return -1;
+    }
+
+    if (!format) format="";
+    ssize_t fmtLen = strlen(format);
+    int ret = map->find(TagFmt(std::make_pair(MapString(tagname, len),
+                                              MapString(format, fmtLen))));
+    if (ret != -1) return ret;
+
+    // call event tag service to arrange for a new tag
+    char *buf = NULL;
+    // Can not use android::base::StringPrintf, asprintf + free instead.
+    static const char command_template[] = "getEventTag name=%s format=\"%s\"";
+    ret = asprintf(&buf, command_template, tagname, format);
+    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;
+            } else {
+                size = ret;
+            }
+        } else {
+            size = ret;
+        }
+        // Ask event log tag service for an allocation
+        if (__send_log_msg(buf, size) >= 0) {
+            buf[size - 1] = '\0';
+            unsigned long val = strtoul(buf, &cp, 10); // return size
+            if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
+                val = strtoul(cp + 1, &cp, 10); // allocated tag number
+                if ((val > 0) && (val < UINT32_MAX) && (*cp == '\t')) {
+                    free(buf);
+                    ret = val;
+                    // cache
+                    map->emplaceUnique(ret, TagFmt(std::make_pair(
+                            MapString(std::string(tagname, len)),
+                            MapString(std::string(format, fmtLen)))));
+                    return ret;
+                }
+            }
+        }
+        free(buf);
+    }
+
+    // Hail Mary
+    ret = map->find(MapString(tagname, len));
+    if (ret == -1) errno = ESRCH;
+    return ret;
+}
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index 4939221..1d7a157 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -612,7 +612,12 @@
 
 bail:
     unlock();
-    return vector[0].iov_len + vector[1].iov_len + vector[2].iov_len;
+    int len = 0;
+    for (i = 0; i < count; ++i) {
+       len += vector[i].iov_len;
+    }
+    return len;
+
 error:
     unlock();
     return -1;
@@ -715,6 +720,12 @@
     return redirectWritev(fd, vector, count);
 }
 
+LIBLOG_HIDDEN ssize_t __send_log_msg(char *buf __unused,
+                                     size_t buf_size __unused)
+{
+    return -ENODEV;
+}
+
 LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio,
                                                 const char *tag __unused,
                                                 int def)
diff --git a/liblog/fake_writer.c b/liblog/fake_writer.c
index dab8bc5..2350673 100644
--- a/liblog/fake_writer.c
+++ b/liblog/fake_writer.c
@@ -46,9 +46,19 @@
     int i;
 
     for (i = 0; i < LOG_ID_MAX; i++) {
-        char buf[sizeof("/dev/log_security")];
+        /*
+         * Known maximum size string, plus an 8 character margin to deal with
+         * possible independent changes to android_log_id_to_name().
+         */
+        char buf[sizeof("/dev/log_security") + 8];
+        if (logFds[i] >= 0) {
+            continue;
+        }
         snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
         logFds[i] = fakeLogOpen(buf, O_WRONLY);
+        if (logFds[i] < 0) {
+            fprintf(stderr, "fakeLogOpen(%s, O_WRONLY) failed\n", buf);
+        }
     }
     return 0;
 }
@@ -66,16 +76,28 @@
                       struct iovec *vec, size_t nr)
 {
     ssize_t ret;
-    int logFd;
+    size_t i;
+    int logFd, len;
 
     if (/*(int)log_id >= 0 &&*/ (int)log_id >= (int)LOG_ID_MAX) {
-        return -EBADF;
+        return -EINVAL;
+    }
+
+    len = 0;
+    for (i = 0; i < nr; ++i) {
+        len += vec[i].iov_len;
+    }
+
+    if (len > LOGGER_ENTRY_MAX_PAYLOAD) {
+        len = LOGGER_ENTRY_MAX_PAYLOAD;
     }
 
     logFd = logFds[(int)log_id];
     ret = TEMP_FAILURE_RETRY(fakeLogWritev(logFd, vec, nr));
     if (ret < 0) {
         ret = -errno;
+    } else if (ret > len) {
+        ret = len;
     }
 
     return ret;
diff --git a/liblog/legacy-ndk-includes/log.h b/liblog/include/android/log.h
similarity index 72%
rename from liblog/legacy-ndk-includes/log.h
rename to liblog/include/android/log.h
index 0ea4c29..9f198fe 100644
--- a/liblog/legacy-ndk-includes/log.h
+++ b/liblog/include/android/log.h
@@ -91,14 +91,22 @@
 /*
  * Send a simple string to the log.
  */
-int __android_log_write(int prio, const char *tag, const char *text);
+int __android_log_write(int prio, const char* tag, const char* text);
 
 /*
  * Send a formatted string to the log, used like printf(fmt,...)
  */
-int __android_log_print(int prio, const char *tag,  const char *fmt, ...)
+int __android_log_print(int prio, const char* tag,  const char* fmt, ...)
 #if defined(__GNUC__)
-    __attribute__ ((format(printf, 3, 4)))
+#ifdef __USE_MINGW_ANSI_STDIO
+#if __USE_MINGW_ANSI_STDIO
+    __attribute__ ((__format__(gnu_printf, 3, 4)))
+#else
+    __attribute__ ((__format__(printf, 3, 4)))
+#endif
+#else
+    __attribute__ ((__format__(printf, 3, 4)))
+#endif
 #endif
     ;
 
@@ -106,18 +114,38 @@
  * A variant of __android_log_print() that takes a va_list to list
  * additional parameters.
  */
-int __android_log_vprint(int prio, const char *tag,
-                         const char *fmt, va_list ap);
+int __android_log_vprint(int prio, const char* tag,
+                         const char* fmt, va_list ap)
+#if defined(__GNUC__)
+#ifdef __USE_MINGW_ANSI_STDIO
+#if __USE_MINGW_ANSI_STDIO
+    __attribute__ ((__format__(gnu_printf, 3, 0)))
+#else
+    __attribute__ ((__format__(printf, 3, 0)))
+#endif
+#else
+    __attribute__ ((__format__(printf, 3, 0)))
+#endif
+#endif
+    ;
 
 /*
- * Log an assertion failure and SIGTRAP the process to have a chance
- * to inspect it, if a debugger is attached. This uses the FATAL priority.
+ * Log an assertion failure and abort the process to have a chance
+ * to inspect it if a debugger is attached. This uses the FATAL priority.
  */
-void __android_log_assert(const char *cond, const char *tag,
-			  const char *fmt, ...)    
+void __android_log_assert(const char* cond, const char* tag,
+                          const char* fmt, ...)
 #if defined(__GNUC__)
-    __attribute__ ((noreturn))
-    __attribute__ ((format(printf, 3, 4)))
+    __attribute__ ((__noreturn__))
+#ifdef __USE_MINGW_ANSI_STDIO
+#if __USE_MINGW_ANSI_STDIO
+    __attribute__ ((__format__(gnu_printf, 3, 4)))
+#else
+    __attribute__ ((__format__(printf, 3, 4)))
+#endif
+#else
+    __attribute__ ((__format__(printf, 3, 4)))
+#endif
 #endif
     ;
 
diff --git a/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
similarity index 90%
rename from include/log/event_tag_map.h
rename to liblog/include/log/event_tag_map.h
index 22e62ec..e57e47b 100644
--- a/include/log/event_tag_map.h
+++ b/liblog/include/log/event_tag_map.h
@@ -58,6 +58,12 @@
 const char* android_lookupEventFormat_len(const EventTagMap* map,
                                           size_t* len, unsigned int tag);
 
+/*
+ * Look up tagname, generate one if necessary, and return a tag
+ */
+int android_lookupEventTagNum(EventTagMap* map, const char* tagname,
+                              const char* format, int prio);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
new file mode 100644
index 0000000..db22211
--- /dev/null
+++ b/liblog/include/log/log.h
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2005-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 _LIBS_LOG_LOG_H
+#define _LIBS_LOG_LOG_H
+
+/* Too many in the ecosystem assume these are included */
+#if !defined(_WIN32)
+#include <pthread.h>
+#endif
+#include <stdint.h>  /* uint16_t, int32_t */
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <android/log.h>
+#include <log/log_id.h>
+#include <log/log_main.h>
+#include <log/log_radio.h>
+#include <log/log_read.h>
+#include <log/log_system.h>
+#include <log/log_time.h>
+#include <log/uio.h> /* helper to define iovec for portability */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * LOG_TAG is the local tag used for the following simplified
+ * logging macros.  You can change this preprocessor definition
+ * before using the other macros to change the tag.
+ */
+
+#ifndef LOG_TAG
+#define LOG_TAG NULL
+#endif
+
+/*
+ * Normally we strip the effects of ALOGV (VERBOSE messages),
+ * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
+ * release builds be defining NDEBUG.  You can modify this (for
+ * example with "#define LOG_NDEBUG 0" at the top of your source
+ * file) to change that behavior.
+ */
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * This file uses ", ## __VA_ARGS__" zero-argument token pasting to
+ * work around issues with debug-only syntax errors in assertions
+ * that are missing format strings.  See commit
+ * 19299904343daf191267564fe32e6cd5c165cd42
+ */
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Event logging.
+ */
+
+/*
+ * The following should not be used directly.
+ */
+
+int __android_log_bwrite(int32_t tag, const void* payload, size_t len);
+int __android_log_btwrite(int32_t tag, char type, const void* payload,
+                          size_t len);
+int __android_log_bswrite(int32_t tag, const char* payload);
+
+#define android_bWriteLog(tag, payload, len) \
+    __android_log_bwrite(tag, payload, len)
+#define android_btWriteLog(tag, type, payload, len) \
+    __android_log_btwrite(tag, type, payload, len)
+
+/*
+ * Event log entry types.
+ */
+#ifndef __AndroidEventLogType_defined
+#define __AndroidEventLogType_defined
+typedef enum {
+    /* Special markers for android_log_list_element type */
+    EVENT_TYPE_LIST_STOP = '\n', /* declare end of list  */
+    EVENT_TYPE_UNKNOWN   = '?',  /* protocol error       */
+
+    /* must match with declaration in java/android/android/util/EventLog.java */
+    EVENT_TYPE_INT       = 0,    /* int32_t */
+    EVENT_TYPE_LONG      = 1,    /* int64_t */
+    EVENT_TYPE_STRING    = 2,
+    EVENT_TYPE_LIST      = 3,
+    EVENT_TYPE_FLOAT     = 4,
+} AndroidEventLogType;
+#endif
+#define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType)
+#define typeof_AndroidEventLogType unsigned char
+
+#ifndef LOG_EVENT_INT
+#define LOG_EVENT_INT(_tag, _value) {                                       \
+        int intBuf = _value;                                                \
+        (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf,            \
+            sizeof(intBuf));                                                \
+    }
+#endif
+#ifndef LOG_EVENT_LONG
+#define LOG_EVENT_LONG(_tag, _value) {                                      \
+        long long longBuf = _value;                                         \
+        (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf,          \
+            sizeof(longBuf));                                               \
+    }
+#endif
+#ifndef LOG_EVENT_FLOAT
+#define LOG_EVENT_FLOAT(_tag, _value) {                                     \
+        float floatBuf = _value;                                            \
+        (void) android_btWriteLog(_tag, EVENT_TYPE_FLOAT, &floatBuf,        \
+            sizeof(floatBuf));                                              \
+    }
+#endif
+#ifndef LOG_EVENT_STRING
+#define LOG_EVENT_STRING(_tag, _value)                                      \
+        (void) __android_log_bswrite(_tag, _value);
+#endif
+
+#ifdef __linux__
+
+#ifndef __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 1
+#elif __ANDROID_API__ > 22 /* > Lollipop */
+#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
+clockid_t android_log_clockid();
+#endif
+
+#endif /* __linux__ */
+
+/* --------------------------------------------------------------------- */
+
+#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
+#elif __ANDROID_API__ > 18 /* > JellyBean */
+#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_CLOSE_INTERFACE
+/*
+ * Release any logger resources (a new log write will immediately re-acquire)
+ *
+ * May be used to clean up File descriptors after a Fork, the resources are
+ * all O_CLOEXEC so wil self clean on exec().
+ */
+void __android_log_close();
+#endif
+
+#ifndef __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 1
+#elif __ANDROID_API__ > 25 /* > OC */
+#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
+
+/*
+ * if last is NULL, caller _must_ provide a consistent value for seconds.
+ *
+ * Return -1 if we can not acquire a lock, which below will permit the logging,
+ * error on allowing a log message through.
+ */
+int __android_log_ratelimit(time_t seconds, time_t* last);
+
+/*
+ * Usage:
+ *
+ *   // Global default and state
+ *   IF_ALOG_RATELIMIT() {
+ *      ALOG*(...);
+ *   }
+ *
+ *   // local state, 10 seconds ratelimit
+ *   static time_t local_state;
+ *   IF_ALOG_RATELIMIT_LOCAL(10, &local_state) {
+ *     ALOG*(...);
+ *   }
+ */
+
+#define IF_ALOG_RATELIMIT() \
+      if (__android_log_ratelimit(0, NULL) > 0)
+#define IF_ALOG_RATELIMIT_LOCAL(seconds, state) \
+      if (__android_log_ratelimit(seconds, state) > 0)
+
+#else
+
+/* No ratelimiting as API unsupported */
+#define IF_ALOG_RATELIMIT() if (1)
+#define IF_ALOG_RATELIMIT_LOCAL(...) if (1)
+
+#endif
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_LOG_H */
diff --git a/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
similarity index 100%
rename from include/log/log_event_list.h
rename to liblog/include/log/log_event_list.h
diff --git a/liblog/include/log/log_frontend.h b/liblog/include/log/log_frontend.h
new file mode 100644
index 0000000..9527779
--- /dev/null
+++ b/liblog/include/log/log_frontend.h
@@ -0,0 +1,34 @@
+/*
+**
+** 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_FRONTEND_H
+#define _LIBS_LOG_FRONTEND_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Logging frontends, bit mask to select features. Function returns selection.
+ */
+#define LOGGER_DEFAULT 0x0
+#define LOGGER_LOGD    0x1
+#define LOGGER_KERNEL  0x2 /* Reserved/Deprecated */
+#define LOGGER_NULL    0x4 /* Does not release resources of other selections */
+#define LOGGER_LOCAL   0x8 /* logs sent to local memory */
+
+/* Both return the selected frontend flag mask, or negative errno */
+int android_set_log_frontend(int frontend_flag);
+int android_get_log_frontend();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_FRONTEND_H */
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
new file mode 100644
index 0000000..3078e4e
--- /dev/null
+++ b/liblog/include/log/log_id.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2005-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 _LIBS_LOG_LOG_ID_H
+#define _LIBS_LOG_LOG_ID_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef log_id_t_defined
+#define log_id_t_defined
+typedef enum log_id {
+    LOG_ID_MIN = 0,
+
+    LOG_ID_MAIN = 0,
+    LOG_ID_RADIO = 1,
+    LOG_ID_EVENTS = 2,
+    LOG_ID_SYSTEM = 3,
+    LOG_ID_CRASH = 4,
+    LOG_ID_SECURITY = 5,
+    LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */
+
+    LOG_ID_MAX
+} log_id_t;
+#endif
+#define sizeof_log_id_t sizeof(typeof_log_id_t)
+#define typeof_log_id_t unsigned char
+
+/*
+ * Send a simple string to the log.
+ */
+int __android_log_buf_write(int bufID, int prio, const char* tag, const char* text);
+int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
+#if defined(__GNUC__)
+    __attribute__((__format__(printf, 4, 5)))
+#endif
+    ;
+
+/*
+ * log_id_t helpers
+ */
+log_id_t android_name_to_log_id(const char* logName);
+const char* android_log_id_to_name(log_id_t log_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_LOG_ID_H */
diff --git a/include/android/log.h b/liblog/include/log/log_main.h
similarity index 70%
rename from include/android/log.h
rename to liblog/include/log/log_main.h
index 5673357..f45397a 100644
--- a/include/android/log.h
+++ b/liblog/include/log/log_main.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2005-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,88 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef _ANDROID_LOG_H
-#define _ANDROID_LOG_H
+#ifndef _LIBS_LOG_LOG_MAIN_H
+#define _LIBS_LOG_LOG_MAIN_H
 
-/******************************************************************
- *
- * IMPORTANT NOTICE:
- *
- *   This file is part of Android's set of stable system headers
- *   exposed by the Android NDK (Native Development Kit) since
- *   platform release 1.5
- *
- *   Third-party source AND binary code relies on the definitions
- *   here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
- *
- *   - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
- *   - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
- *   - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
- *   - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
- */
-
-/*
- * Support routines to send messages to the Android in-kernel log buffer,
- * which can later be accessed through the 'logcat' utility.
- *
- * Each log message must have
- *   - a priority
- *   - a log tag
- *   - some text
- *
- * The tag normally corresponds to the component that emits the log message,
- * and should be reasonably small.
- *
- * Log message text may be truncated to less than an implementation-specific
- * limit (e.g. 1023 characters max).
- *
- * Note that a newline character ("\n") will be appended automatically to your
- * log message, if not already there. It is not possible to send several messages
- * and have them appear on a single line in logcat.
- *
- * PLEASE USE LOGS WITH MODERATION:
- *
- *  - Sending log messages eats CPU and slow down your application and the
- *    system.
- *
- *  - The circular log buffer is pretty small (<64KB), sending many messages
- *    might push off other important log messages from the rest of the system.
- *
- *  - In release builds, only send log messages to account for exceptional
- *    conditions.
- *
- * NOTE: These functions MUST be implemented by /system/lib/liblog.so
- */
-
-#include <stdarg.h>
+#include <android/log.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /*
- * This file uses ", ## __VA_ARGS__" zero-argument token pasting to
- * work around issues with debug-only syntax errors in assertions
- * that are missing format strings.  See commit
- * 19299904343daf191267564fe32e6cd5c165cd42
- */
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
-#endif
-
-#ifndef __predict_false
-#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
-#endif
-
-/*
- * LOG_TAG is the local tag used for the following simplified
- * logging macros.  You must set this preprocessor definition,
- * or more tenuously supply a variable definition, before using
- * the macros.
- */
-
-/*
  * Normally we strip the effects of ALOGV (VERBOSE messages),
  * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
  * release builds be defining NDEBUG.  You can modify this (for
@@ -111,52 +39,32 @@
 #endif
 #endif
 
-/*
- * Android log priority values, in ascending priority order.
- */
-#ifndef __android_LogPriority_defined
-#define __android_LogPriority_defined
-typedef enum android_LogPriority {
-    ANDROID_LOG_UNKNOWN = 0,
-    ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */
-    ANDROID_LOG_VERBOSE,
-    ANDROID_LOG_DEBUG,
-    ANDROID_LOG_INFO,
-    ANDROID_LOG_WARN,
-    ANDROID_LOG_ERROR,
-    ANDROID_LOG_FATAL,
-    ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */
-} android_LogPriority;
-#endif
+/* --------------------------------------------------------------------- */
 
 /*
- * Send a simple string to the log.
+ * This file uses ", ## __VA_ARGS__" zero-argument token pasting to
+ * work around issues with debug-only syntax errors in assertions
+ * that are missing format strings.  See commit
+ * 19299904343daf191267564fe32e6cd5c165cd42
  */
-int __android_log_write(int prio, const char* tag, const char* text);
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+#endif
+
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
 
 #define android_writeLog(prio, tag, text) \
     __android_log_write(prio, tag, text)
 
-/*
- * Send a formatted string to the log, used like printf(fmt,...)
- */
-int __android_log_print(int prio, const char* tag,  const char* fmt, ...)
-#if defined(__GNUC__)
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-    __attribute__ ((format(gnu_printf, 3, 4)))
-#else
-    __attribute__ ((format(printf, 3, 4)))
-#endif
-#else
-    __attribute__ ((format(printf, 3, 4)))
-#endif
-#endif
-    ;
-
 #define android_printLog(prio, tag, ...) \
     __android_log_print(prio, tag, __VA_ARGS__)
 
+#define android_vprintLog(prio, cond, tag, ...) \
+    __android_log_vprint(prio, tag, __VA_ARGS__)
+
 /*
  * Log macro that allows you to specify a number for the priority.
  */
@@ -166,28 +74,6 @@
 #endif
 
 /*
- * A variant of __android_log_print() that takes a va_list to list
- * additional parameters.
- */
-int __android_log_vprint(int prio, const char* tag,
-                         const char* fmt, va_list ap)
-#if defined(__GNUC__)
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-    __attribute__ ((format(gnu_printf, 3, 0)))
-#else
-    __attribute__ ((format(printf, 3, 0)))
-#endif
-#else
-    __attribute__ ((format(printf, 3, 0)))
-#endif
-#endif
-    ;
-
-#define android_vprintLog(prio, cond, tag, ...) \
-    __android_log_vprint(prio, tag, __VA_ARGS__)
-
-/*
  * Log macro that allows you to pass in a varargs ("args" is a va_list).
  */
 #ifndef LOG_PRI_VA
@@ -195,25 +81,7 @@
     android_vprintLog(priority, NULL, tag, fmt, args)
 #endif
 
-/*
- * Log an assertion failure and abort the process to have a chance
- * to inspect it if a debugger is attached. This uses the FATAL priority.
- */
-void __android_log_assert(const char* cond, const char* tag,
-                          const char* fmt, ...)
-#if defined(__GNUC__)
-    __attribute__ ((__noreturn__))
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-    __attribute__ ((format(gnu_printf, 3, 4)))
-#else
-    __attribute__ ((format(printf, 3, 4)))
-#endif
-#else
-    __attribute__ ((format(printf, 3, 4)))
-#endif
-#endif
-    ;
+/* --------------------------------------------------------------------- */
 
 /* XXX Macros to work around syntax errors in places where format string
  * arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF
@@ -524,4 +392,4 @@
 }
 #endif
 
-#endif /* _ANDROID_LOG_H */
+#endif /* _LIBS_LOG_LOG_MAIN_H */
diff --git a/liblog/include/log/log_radio.h b/liblog/include/log/log_radio.h
new file mode 100644
index 0000000..430e522
--- /dev/null
+++ b/liblog/include/log/log_radio.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2005-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 _LIBS_LOG_LOG_RADIO_H
+#define _LIBS_LOG_LOG_RADIO_H
+
+#include <android/log.h>
+#include <log/log_id.h>
+
+/*
+ * Normally we strip the effects of ALOGV (VERBOSE messages),
+ * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
+ * release builds be defining NDEBUG.  You can modify this (for
+ * example with "#define LOG_NDEBUG 0" at the top of your source
+ * file) to change that behavior.
+ */
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
+
+/*
+ * Simplified macro to send a verbose radio log message using current LOG_TAG.
+ */
+#ifndef RLOGV
+#define __RLOGV(...) \
+    ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#if LOG_NDEBUG
+#define RLOGV(...) do { if (0) { __RLOGV(__VA_ARGS__); } } while (0)
+#else
+#define RLOGV(...) __RLOGV(__VA_ARGS__)
+#endif
+#endif
+
+#ifndef RLOGV_IF
+#if LOG_NDEBUG
+#define RLOGV_IF(cond, ...)   ((void)0)
+#else
+#define RLOGV_IF(cond, ...) \
+    ( (__predict_false(cond)) \
+    ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug radio log message using  current LOG_TAG.
+ */
+#ifndef RLOGD
+#define RLOGD(...) \
+    ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef RLOGD_IF
+#define RLOGD_IF(cond, ...) \
+    ( (__predict_false(cond)) \
+    ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an info radio log message using  current LOG_TAG.
+ */
+#ifndef RLOGI
+#define RLOGI(...) \
+    ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef RLOGI_IF
+#define RLOGI_IF(cond, ...) \
+    ( (__predict_false(cond)) \
+    ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send a warning radio log message using current LOG_TAG.
+ */
+#ifndef RLOGW
+#define RLOGW(...) \
+    ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef RLOGW_IF
+#define RLOGW_IF(cond, ...) \
+    ( (__predict_false(cond)) \
+    ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an error radio log message using current LOG_TAG.
+ */
+#ifndef RLOGE
+#define RLOGE(...) \
+    ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef RLOGE_IF
+#define RLOGE_IF(cond, ...) \
+    ( (__predict_false(cond)) \
+    ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+
+#endif /* _LIBS_LOG_LOG_RADIO_H */
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
new file mode 100644
index 0000000..6a44b56
--- /dev/null
+++ b/liblog/include/log/log_read.h
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2005-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 _LIBS_LOG_LOG_READ_H
+#define _LIBS_LOG_LOG_READ_H
+
+/* deal with possible sys/cdefs.h conflict with fcntl.h */
+#ifdef __unused
+#define __unused_defined __unused
+#undef __unused
+#endif
+
+#include <fcntl.h> /* Pick up O_* macros */
+
+/* restore definitions from above */
+#ifdef __unused_defined
+#define __unused __attribute__((__unused__))
+#endif
+
+#include <stdint.h>
+
+#include <log/log_id.h>
+#include <log/log_time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Native log reading interface section. See logcat for sample code.
+ *
+ * The preferred API is an exec of logcat. Likely uses of this interface
+ * are if native code suffers from exec or filtration being too costly,
+ * access to raw information, or parsing is an issue.
+ */
+
+/*
+ * The userspace structure for version 1 of the logger_entry ABI.
+ */
+#ifndef __struct_logger_entry_defined
+#define __struct_logger_entry_defined
+struct logger_entry {
+    uint16_t    len;    /* length of the payload */
+    uint16_t    __pad;  /* no matter what, we get 2 bytes of padding */
+    int32_t     pid;    /* generating process's pid */
+    int32_t     tid;    /* generating process's tid */
+    int32_t     sec;    /* seconds since Epoch */
+    int32_t     nsec;   /* nanoseconds */
+#ifndef __cplusplus
+    char        msg[0]; /* the entry's payload */
+#endif
+};
+#endif
+
+/*
+ * The userspace structure for version 2 of the logger_entry ABI.
+ */
+#ifndef __struct_logger_entry_v2_defined
+#define __struct_logger_entry_v2_defined
+struct logger_entry_v2 {
+    uint16_t    len;       /* length of the payload */
+    uint16_t    hdr_size;  /* sizeof(struct logger_entry_v2) */
+    int32_t     pid;       /* generating process's pid */
+    int32_t     tid;       /* generating process's tid */
+    int32_t     sec;       /* seconds since Epoch */
+    int32_t     nsec;      /* nanoseconds */
+    uint32_t    euid;      /* effective UID of logger */
+#ifndef __cplusplus
+    char        msg[0];    /* the entry's payload */
+#endif
+} __attribute__((__packed__));
+#endif
+
+/*
+ * The userspace structure for version 3 of the logger_entry ABI.
+ */
+#ifndef __struct_logger_entry_v3_defined
+#define __struct_logger_entry_v3_defined
+struct logger_entry_v3 {
+    uint16_t    len;       /* length of the payload */
+    uint16_t    hdr_size;  /* sizeof(struct logger_entry_v3) */
+    int32_t     pid;       /* generating process's pid */
+    int32_t     tid;       /* generating process's tid */
+    int32_t     sec;       /* seconds since Epoch */
+    int32_t     nsec;      /* nanoseconds */
+    uint32_t    lid;       /* log id of the payload */
+#ifndef __cplusplus
+    char        msg[0];    /* the entry's payload */
+#endif
+} __attribute__((__packed__));
+#endif
+
+/*
+ * The userspace structure for version 4 of the logger_entry ABI.
+ */
+#ifndef __struct_logger_entry_v4_defined
+#define __struct_logger_entry_v4_defined
+struct logger_entry_v4 {
+    uint16_t    len;       /* length of the payload */
+    uint16_t    hdr_size;  /* sizeof(struct logger_entry_v4) */
+    int32_t     pid;       /* generating process's pid */
+    uint32_t    tid;       /* generating process's tid */
+    uint32_t    sec;       /* seconds since Epoch */
+    uint32_t    nsec;      /* nanoseconds */
+    uint32_t    lid;       /* log id of the payload, bottom 4 bits currently */
+    uint32_t    uid;       /* generating process's uid */
+#ifndef __cplusplus
+    char        msg[0];    /* the entry's payload */
+#endif
+};
+#endif
+
+/*
+ * The maximum size of the log entry payload that can be
+ * written to the logger. An attempt to write more than
+ * this amount will result in a truncated log entry.
+ */
+#define LOGGER_ENTRY_MAX_PAYLOAD 4068
+
+/*
+ * The maximum size of a log entry which can be read.
+ * An attempt to read less than this amount may result
+ * in read() returning EINVAL.
+ */
+#define LOGGER_ENTRY_MAX_LEN    (5*1024)
+
+#ifndef __struct_log_msg_defined
+#define __struct_log_msg_defined
+struct log_msg {
+    union {
+        unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
+        struct logger_entry_v4 entry;
+        struct logger_entry_v4 entry_v4;
+        struct logger_entry_v3 entry_v3;
+        struct logger_entry_v2 entry_v2;
+        struct logger_entry    entry_v1;
+    } __attribute__((aligned(4)));
+#ifdef __cplusplus
+    /* Matching log_time operators */
+    bool operator== (const log_msg& T) const
+    {
+        return (entry.sec == T.entry.sec) && (entry.nsec == T.entry.nsec);
+    }
+    bool operator!= (const log_msg& T) const
+    {
+        return !(*this == T);
+    }
+    bool operator< (const log_msg& T) const
+    {
+        return (entry.sec < T.entry.sec)
+            || ((entry.sec == T.entry.sec)
+             && (entry.nsec < T.entry.nsec));
+    }
+    bool operator>= (const log_msg& T) const
+    {
+        return !(*this < T);
+    }
+    bool operator> (const log_msg& T) const
+    {
+        return (entry.sec > T.entry.sec)
+            || ((entry.sec == T.entry.sec)
+             && (entry.nsec > T.entry.nsec));
+    }
+    bool operator<= (const log_msg& T) const
+    {
+        return !(*this > T);
+    }
+    uint64_t nsec() const
+    {
+        return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec;
+    }
+
+    /* packet methods */
+    log_id_t id()
+    {
+        return static_cast<log_id_t>(entry.lid);
+    }
+    char* msg()
+    {
+        unsigned short hdr_size = entry.hdr_size;
+        if (!hdr_size) {
+            hdr_size = sizeof(entry_v1);
+        }
+        if ((hdr_size < sizeof(entry_v1)) || (hdr_size > sizeof(entry))) {
+            return NULL;
+        }
+        return reinterpret_cast<char*>(buf) + hdr_size;
+    }
+    unsigned int len()
+    {
+        return (entry.hdr_size ?
+                    entry.hdr_size :
+                    static_cast<uint16_t>(sizeof(entry_v1))) +
+               entry.len;
+    }
+#endif
+};
+#endif
+
+#ifndef __ANDROID_USE_LIBLOG_READER_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_READER_INTERFACE 3
+#elif __ANDROID_API__ > 23 /* > Marshmallow */
+#define __ANDROID_USE_LIBLOG_READER_INTERFACE 3
+#elif __ANDROID_API__ > 22 /* > Lollipop */
+#define __ANDROID_USE_LIBLOG_READER_INTERFACE 2
+#elif __ANDROID_API__ > 19 /* > KitKat */
+#define __ANDROID_USE_LIBLOG_READER_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_READER_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_READER_INTERFACE
+
+struct logger;
+
+log_id_t android_logger_get_id(struct logger* logger);
+
+int android_logger_clear(struct logger* logger);
+long android_logger_get_log_size(struct logger* logger);
+int android_logger_set_log_size(struct logger* logger, unsigned long size);
+long android_logger_get_log_readable_size(struct logger* logger);
+int android_logger_get_log_version(struct logger* logger);
+
+struct logger_list;
+
+#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 1
+ssize_t android_logger_get_statistics(struct logger_list* logger_list,
+                                      char* buf, size_t len);
+ssize_t android_logger_get_prune_list(struct logger_list* logger_list,
+                                      char* buf, size_t len);
+int android_logger_set_prune_list(struct logger_list* logger_list,
+                                  char* buf, size_t len);
+#endif
+
+#define ANDROID_LOG_RDONLY   O_RDONLY
+#define ANDROID_LOG_WRONLY   O_WRONLY
+#define ANDROID_LOG_RDWR     O_RDWR
+#define ANDROID_LOG_ACCMODE  O_ACCMODE
+#ifndef O_NONBLOCK
+#define ANDROID_LOG_NONBLOCK 0x00000800
+#else
+#define ANDROID_LOG_NONBLOCK O_NONBLOCK
+#endif
+#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 2
+#define ANDROID_LOG_WRAP     0x40000000 /* Block until buffer about to wrap */
+#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
+#endif
+#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 1
+#define ANDROID_LOG_PSTORE   0x80000000
+#endif
+
+struct logger_list* android_logger_list_alloc(int mode,
+                                              unsigned int tail,
+                                              pid_t pid);
+struct logger_list* android_logger_list_alloc_time(int mode,
+                                                   log_time start,
+                                                   pid_t pid);
+void android_logger_list_free(struct logger_list* logger_list);
+/* In the purest sense, the following two are orthogonal interfaces */
+int android_logger_list_read(struct logger_list* logger_list,
+                             struct log_msg* log_msg);
+
+/* Multiple log_id_t opens */
+struct logger* android_logger_open(struct logger_list* logger_list,
+                                   log_id_t id);
+#define android_logger_close android_logger_free
+/* Single log_id_t open */
+struct logger_list* android_logger_list_open(log_id_t id,
+                                             int mode,
+                                             unsigned int tail,
+                                             pid_t pid);
+#define android_logger_list_close android_logger_list_free
+
+#endif /* __ANDROID_USE_LIBLOG_READER_INTERFACE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_LOG_H */
diff --git a/liblog/include/log/log_system.h b/liblog/include/log/log_system.h
new file mode 100644
index 0000000..394a106
--- /dev/null
+++ b/liblog/include/log/log_system.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2005-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 _LIBS_LOG_LOG_SYSTEM_H
+#define _LIBS_LOG_LOG_SYSTEM_H
+
+#include <android/log.h>
+#include <log/log_id.h>
+
+/*
+ * Normally we strip the effects of ALOGV (VERBOSE messages),
+ * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
+ * release builds be defining NDEBUG.  You can modify this (for
+ * example with "#define LOG_NDEBUG 0" at the top of your source
+ * file) to change that behavior.
+ */
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
+
+/*
+ * Simplified macro to send a verbose system log message using current LOG_TAG.
+ */
+#ifndef SLOGV
+#define __SLOGV(...) \
+    ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#if LOG_NDEBUG
+#define SLOGV(...) do { if (0) { __SLOGV(__VA_ARGS__); } } while (0)
+#else
+#define SLOGV(...) __SLOGV(__VA_ARGS__)
+#endif
+#endif
+
+#ifndef SLOGV_IF
+#if LOG_NDEBUG
+#define SLOGV_IF(cond, ...)   ((void)0)
+#else
+#define SLOGV_IF(cond, ...) \
+    ( (__predict_false(cond)) \
+    ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug system log message using current LOG_TAG.
+ */
+#ifndef SLOGD
+#define SLOGD(...) \
+    ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGD_IF
+#define SLOGD_IF(cond, ...) \
+    ( (__predict_false(cond)) \
+    ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an info system log message using current LOG_TAG.
+ */
+#ifndef SLOGI
+#define SLOGI(...) \
+    ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGI_IF
+#define SLOGI_IF(cond, ...) \
+    ( (__predict_false(cond)) \
+    ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send a warning system log message using current LOG_TAG.
+ */
+#ifndef SLOGW
+#define SLOGW(...) \
+    ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGW_IF
+#define SLOGW_IF(cond, ...) \
+    ( (__predict_false(cond)) \
+    ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an error system log message using current LOG_TAG.
+ */
+#ifndef SLOGE
+#define SLOGE(...) \
+    ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGE_IF
+#define SLOGE_IF(cond, ...) \
+    ( (__predict_false(cond)) \
+    ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+
+#endif /* _LIBS_LOG_LOG_SYSTEM_H */
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
new file mode 100644
index 0000000..900dc1b
--- /dev/null
+++ b/liblog/include/log/log_time.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2005-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 _LIBS_LOG_LOG_TIME_H
+#define _LIBS_LOG_LOG_TIME_H
+
+#include <stdint.h>
+#include <time.h>
+
+/* struct log_time is a wire-format variant of struct timespec */
+#define NS_PER_SEC 1000000000ULL
+
+#ifndef __struct_log_time_defined
+#define __struct_log_time_defined
+
+#ifdef __cplusplus
+
+/*
+ * NB: we did NOT define a copy constructor. This will result in structure
+ * no longer being compatible with pass-by-value which is desired
+ * efficient behavior. Also, pass-by-reference breaks C/C++ ABI.
+ */
+struct log_time {
+public:
+    uint32_t tv_sec; /* good to Feb 5 2106 */
+    uint32_t tv_nsec;
+
+    static const uint32_t tv_sec_max = 0xFFFFFFFFUL;
+    static const uint32_t tv_nsec_max = 999999999UL;
+
+    log_time(const timespec& T)
+    {
+        tv_sec = static_cast<uint32_t>(T.tv_sec);
+        tv_nsec = static_cast<uint32_t>(T.tv_nsec);
+    }
+    log_time(uint32_t sec, uint32_t nsec)
+    {
+        tv_sec = sec;
+        tv_nsec = nsec;
+    }
+#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+#define __struct_log_time_private_defined
+    static const timespec EPOCH;
+#endif
+    log_time()
+    {
+    }
+#ifdef __linux__
+    log_time(clockid_t id)
+    {
+        timespec T;
+        clock_gettime(id, &T);
+        tv_sec = static_cast<uint32_t>(T.tv_sec);
+        tv_nsec = static_cast<uint32_t>(T.tv_nsec);
+    }
+#endif
+    log_time(const char* T)
+    {
+        const uint8_t* c = reinterpret_cast<const uint8_t*>(T);
+        tv_sec = c[0] |
+                 (static_cast<uint32_t>(c[1]) << 8) |
+                 (static_cast<uint32_t>(c[2]) << 16) |
+                 (static_cast<uint32_t>(c[3]) << 24);
+        tv_nsec = c[4] |
+                  (static_cast<uint32_t>(c[5]) << 8) |
+                  (static_cast<uint32_t>(c[6]) << 16) |
+                  (static_cast<uint32_t>(c[7]) << 24);
+    }
+
+    /* timespec */
+    bool operator== (const timespec& T) const
+    {
+        return (tv_sec == static_cast<uint32_t>(T.tv_sec))
+            && (tv_nsec == static_cast<uint32_t>(T.tv_nsec));
+    }
+    bool operator!= (const timespec& T) const
+    {
+        return !(*this == T);
+    }
+    bool operator< (const timespec& T) const
+    {
+        return (tv_sec < static_cast<uint32_t>(T.tv_sec))
+            || ((tv_sec == static_cast<uint32_t>(T.tv_sec))
+                && (tv_nsec < static_cast<uint32_t>(T.tv_nsec)));
+    }
+    bool operator>= (const timespec& T) const
+    {
+        return !(*this < T);
+    }
+    bool operator> (const timespec& T) const
+    {
+        return (tv_sec > static_cast<uint32_t>(T.tv_sec))
+            || ((tv_sec == static_cast<uint32_t>(T.tv_sec))
+                && (tv_nsec > static_cast<uint32_t>(T.tv_nsec)));
+    }
+    bool operator<= (const timespec& T) const
+    {
+        return !(*this > T);
+    }
+
+#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+    log_time operator-= (const timespec& T);
+    log_time operator- (const timespec& T) const
+    {
+        log_time local(*this);
+        return local -= T;
+    }
+    log_time operator+= (const timespec& T);
+    log_time operator+ (const timespec& T) const
+    {
+        log_time local(*this);
+        return local += T;
+    }
+#endif
+
+    /* log_time */
+    bool operator== (const log_time& T) const
+    {
+        return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec);
+    }
+    bool operator!= (const log_time& T) const
+    {
+        return !(*this == T);
+    }
+    bool operator< (const log_time& T) const
+    {
+        return (tv_sec < T.tv_sec)
+            || ((tv_sec == T.tv_sec) && (tv_nsec < T.tv_nsec));
+    }
+    bool operator>= (const log_time& T) const
+    {
+        return !(*this < T);
+    }
+    bool operator> (const log_time& T) const
+    {
+        return (tv_sec > T.tv_sec)
+            || ((tv_sec == T.tv_sec) && (tv_nsec > T.tv_nsec));
+    }
+    bool operator<= (const log_time& T) const
+    {
+        return !(*this > T);
+    }
+
+#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+    log_time operator-= (const log_time& T);
+    log_time operator- (const log_time& T) const
+    {
+        log_time local(*this);
+        return local -= T;
+    }
+    log_time operator+= (const log_time& T);
+    log_time operator+ (const log_time& T) const
+    {
+        log_time local(*this);
+        return local += T;
+    }
+#endif
+
+    uint64_t nsec() const
+    {
+        return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
+    }
+
+#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+    static const char default_format[];
+
+    /* Add %#q for the fraction of a second to the standard library functions */
+    char* strptime(const char* s, const char* format = default_format);
+#endif
+} __attribute__((__packed__));
+
+#else
+
+typedef struct log_time {
+    uint32_t tv_sec;
+    uint32_t tv_nsec;
+} __attribute__((__packed__)) log_time;
+
+#endif
+
+#endif
+
+#endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/liblog/include/log/logd.h b/liblog/include/log/logd.h
new file mode 100644
index 0000000..77400ca
--- /dev/null
+++ b/liblog/include/log/logd.h
@@ -0,0 +1,5 @@
+#ifndef _LIBS_LOG_LOGD_H
+#define _LIBS_LOG_LOGD_H
+#include <log/log.h>
+#warning "Deprecated: do not include log/logd.h, use log/log.h instead"
+#endif /*_LIBS_LOG_LOGD_H*/
diff --git a/liblog/include/log/logger.h b/liblog/include/log/logger.h
new file mode 100644
index 0000000..1bf2d17
--- /dev/null
+++ b/liblog/include/log/logger.h
@@ -0,0 +1,5 @@
+#ifndef _LIBS_LOG_LOGGER_H
+#define _LIBS_LOG_LOGGER_H
+#include <log/log.h>
+#warning "Deprecated: do not include log/logger.h, use log/log.h instead"
+#endif /*_LIBS_LOG_LOGGER_H*/
diff --git a/include/log/logprint.h b/liblog/include/log/logprint.h
similarity index 95%
rename from include/log/logprint.h
rename to liblog/include/log/logprint.h
index 3509e7f..5b99c3c 100644
--- a/include/log/logprint.h
+++ b/liblog/include/log/logprint.h
@@ -42,11 +42,13 @@
     FORMAT_MODIFIER_TIME_USEC, /* switches from msec to usec time precision */
     FORMAT_MODIFIER_PRINTABLE, /* converts non-printable to printable escapes */
     FORMAT_MODIFIER_YEAR,      /* Adds year to date */
-    FORMAT_MODIFIER_ZONE,      /* Adds zone to date */
+    FORMAT_MODIFIER_ZONE,      /* Adds zone to date, + UTC */
     FORMAT_MODIFIER_EPOCH,     /* Print time as seconds since Jan 1 1970 */
     FORMAT_MODIFIER_MONOTONIC, /* Print cpu time as seconds since start */
     FORMAT_MODIFIER_UID,       /* Adds uid */
     FORMAT_MODIFIER_DESCRIPT,  /* Adds descriptive */
+    /* private, undocumented */
+    FORMAT_MODIFIER_TIME_NSEC, /* switches from msec to nsec time precision */
 } AndroidLogPrintFormat;
 
 typedef struct AndroidLogFormat_t AndroidLogFormat;
diff --git a/include/log/uio.h b/liblog/include/log/uio.h
similarity index 100%
rename from include/log/uio.h
rename to liblog/include/log/uio.h
diff --git a/include/private/android_logger.h b/liblog/include/private/android_logger.h
similarity index 100%
rename from include/private/android_logger.h
rename to liblog/include/private/android_logger.h
diff --git a/liblog/include_vndk/android b/liblog/include_vndk/android
new file mode 120000
index 0000000..a3c0320
--- /dev/null
+++ b/liblog/include_vndk/android
@@ -0,0 +1 @@
+../include/android
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log.h b/liblog/include_vndk/log/log.h
new file mode 100644
index 0000000..f93b377
--- /dev/null
+++ b/liblog/include_vndk/log/log.h
@@ -0,0 +1,23 @@
+/*Special log.h file for VNDK linking modules*/
+
+#ifndef _LIBS_LOG_LOG_H
+#define _LIBS_LOG_LOG_H
+
+#include <android/log.h>
+#include <log/log_id.h>
+#include <log/log_main.h>
+#include <log/log_radio.h>
+#include <log/log_read.h>
+#include <log/log_time.h>
+
+/*
+ * LOG_TAG is the local tag used for the following simplified
+ * logging macros.  You can change this preprocessor definition
+ * before using the other macros to change the tag.
+ */
+
+#ifndef LOG_TAG
+#define LOG_TAG NULL
+#endif
+
+#endif /*_LIBS_LOG_LOG_H*/
diff --git a/liblog/include_vndk/log/log_id.h b/liblog/include_vndk/log/log_id.h
new file mode 120000
index 0000000..dce92b9
--- /dev/null
+++ b/liblog/include_vndk/log/log_id.h
@@ -0,0 +1 @@
+../../include/log/log_id.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_main.h b/liblog/include_vndk/log/log_main.h
new file mode 120000
index 0000000..f2ec018
--- /dev/null
+++ b/liblog/include_vndk/log/log_main.h
@@ -0,0 +1 @@
+../../include/log/log_main.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_radio.h b/liblog/include_vndk/log/log_radio.h
new file mode 120000
index 0000000..1e12b32
--- /dev/null
+++ b/liblog/include_vndk/log/log_radio.h
@@ -0,0 +1 @@
+../../include/log/log_radio.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_read.h b/liblog/include_vndk/log/log_read.h
new file mode 120000
index 0000000..01de8b9
--- /dev/null
+++ b/liblog/include_vndk/log/log_read.h
@@ -0,0 +1 @@
+../../include/log/log_read.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_time.h b/liblog/include_vndk/log/log_time.h
new file mode 120000
index 0000000..abfe439
--- /dev/null
+++ b/liblog/include_vndk/log/log_time.h
@@ -0,0 +1 @@
+../../include/log/log_time.h
\ No newline at end of file
diff --git a/liblog/local_logger.c b/liblog/local_logger.c
new file mode 100644
index 0000000..d504342
--- /dev/null
+++ b/liblog/local_logger.c
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#if !defined(__MINGW32__)
+#include <pwd.h>
+#endif
+#include <sched.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <log/uio.h>
+
+#include <cutils/list.h> /* template, no library dependency */
+#include <log/log_frontend.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+#include <system/thread_defs.h>
+
+#include "config_read.h"
+#include "config_write.h"
+#include "log_portability.h"
+#include "logger.h"
+
+static const char baseServiceName[] = "android.logd";
+
+static int writeToLocalInit();
+static int writeToLocalAvailable(log_id_t logId);
+static void writeToLocalReset();
+static int writeToLocalWrite(log_id_t logId, struct timespec *ts,
+                             struct iovec *vec, size_t nr);
+
+LIBLOG_HIDDEN struct android_log_transport_write localLoggerWrite = {
+    .node = { &localLoggerWrite.node, &localLoggerWrite.node },
+    .context.private = NULL,
+    .name = "local",
+    .available = writeToLocalAvailable,
+    .open = writeToLocalInit,
+    .close = writeToLocalReset,
+    .write = writeToLocalWrite,
+};
+
+static int writeToLocalVersion(struct android_log_logger *logger,
+                               struct android_log_transport_context *transp);
+static int writeToLocalRead(struct android_log_logger_list *logger_list,
+                            struct android_log_transport_context *transp,
+                            struct log_msg *log_msg);
+static int writeToLocalPoll(struct android_log_logger_list *logger_list,
+                            struct android_log_transport_context *transp);
+static void writeToLocalClose(struct android_log_logger_list *logger_list,
+                              struct android_log_transport_context *transp);
+static int writeToLocalClear(struct android_log_logger *logger,
+                             struct android_log_transport_context *transp);
+static ssize_t writeToLocalGetSize(
+        struct android_log_logger *logger,
+        struct android_log_transport_context *transp);
+static ssize_t writeToLocalSetSize(
+        struct android_log_logger *logger,
+        struct android_log_transport_context *transp __unused,
+        size_t size);
+static ssize_t writeToLocalGetReadbleSize(
+        struct android_log_logger *logger,
+        struct android_log_transport_context *transp);
+
+struct android_log_transport_read localLoggerRead = {
+    .node = { &localLoggerRead.node, &localLoggerRead.node },
+    .name = "local",
+    .available = writeToLocalAvailable,
+    .version = writeToLocalVersion,
+    .read = writeToLocalRead,
+    .poll = writeToLocalPoll,
+    .close = writeToLocalClose,
+    .clear = writeToLocalClear,
+    .getSize = writeToLocalGetSize,
+    .setSize = writeToLocalSetSize,
+    .getReadableSize = writeToLocalGetReadbleSize,
+    .getPrune = NULL,
+    .setPrune = NULL,
+    .getStats = NULL,
+};
+
+struct LogBufferElement {
+  struct listnode node;
+  log_id_t logId;
+  pid_t tid;
+  log_time timestamp;
+  unsigned short len;
+  char msg[];
+};
+
+static const size_t MAX_SIZE_DEFAULT = 32768;
+
+/*
+ * Number of log buffers we support with the following assumption:
+ *  . . .
+ *   LOG_ID_SECURITY = 5, // security logs go to the system logs only
+ *   LOG_ID_KERNEL = 6,   // place last, third-parties can not use it
+ *   LOG_ID_MAX
+ * } log_id_t;
+ *
+ * Confirm the following should <log/log_id.h> be adjusted in the future.
+ */
+#define NUMBER_OF_LOG_BUFFERS ((LOG_ID_SECURITY == (LOG_ID_MAX - 2)) ? \
+                                  LOG_ID_SECURITY : \
+                                  LOG_ID_KERNEL)
+#define BLOCK_LOG_BUFFERS(id) (((id) == LOG_ID_SECURITY) || \
+                               ((id) == LOG_ID_KERNEL))
+
+static struct LogBuffer {
+  struct listnode head;
+  pthread_rwlock_t listLock;
+  char *serviceName; /* Also indicates ready by having a value */
+  /* Order and proximity important for memset */
+  size_t number[NUMBER_OF_LOG_BUFFERS];         /* clear memset          */
+  size_t size[NUMBER_OF_LOG_BUFFERS];           /* clear memset          */
+  size_t totalSize[NUMBER_OF_LOG_BUFFERS];      /* init memset           */
+  size_t maxSize[NUMBER_OF_LOG_BUFFERS];        /* init MAX_SIZE_DEFAULT */
+  struct listnode *last[NUMBER_OF_LOG_BUFFERS]; /* init &head            */
+} logbuf = {
+  .head = { &logbuf.head, &logbuf.head },
+  .listLock = PTHREAD_RWLOCK_INITIALIZER,
+};
+
+static void LogBufferInit(struct LogBuffer *log) {
+  size_t i;
+
+  pthread_rwlock_wrlock(&log->listLock);
+  list_init(&log->head);
+  memset(log->number, 0,
+    sizeof(log->number) + sizeof(log->size) + sizeof(log->totalSize));
+  for (i = 0; i < NUMBER_OF_LOG_BUFFERS; ++i) {
+    log->maxSize[i] = MAX_SIZE_DEFAULT;
+    log->last[i] = &log->head;
+  }
+#ifdef __BIONIC__
+  asprintf(&log->serviceName, "%s@%d:%d", baseServiceName,
+           __android_log_uid(), getpid());
+#else
+  char buffer[sizeof(baseServiceName) + 1 + 5 + 1 + 5 + 8];
+  snprintf(buffer, sizeof(buffer), "%s@%d:%d", baseServiceName,
+                                   __android_log_uid(), getpid());
+  log->serviceName = strdup(buffer);
+#endif
+  pthread_rwlock_unlock(&log->listLock);
+}
+
+static void LogBufferClear(struct LogBuffer *log) {
+  size_t i;
+  struct listnode *node;
+
+  pthread_rwlock_wrlock(&log->listLock);
+  memset(log->number, 0, sizeof(log->number) + sizeof(log->size));
+  for (i = 0; i < NUMBER_OF_LOG_BUFFERS; ++i) {
+    log->last[i] = &log->head;
+  }
+  while ((node = list_head(&log->head)) != &log->head) {
+    struct LogBufferElement *element;
+
+    element = node_to_item(node, struct LogBufferElement, node);
+    list_remove(node);
+    free(element);
+  }
+  pthread_rwlock_unlock(&log->listLock);
+}
+
+static inline void LogBufferFree(struct LogBuffer *log) {
+  pthread_rwlock_wrlock(&log->listLock);
+  free(log->serviceName);
+  log->serviceName = NULL;
+  pthread_rwlock_unlock(&log->listLock);
+  LogBufferClear(log);
+}
+
+static int LogBufferLog(struct LogBuffer *log,
+                        struct LogBufferElement *element) {
+  log_id_t logId = element->logId;
+
+  pthread_rwlock_wrlock(&log->listLock);
+  log->number[logId]++;
+  log->size[logId] += element->len;
+  log->totalSize[logId] += element->len;
+  /* prune entry(s) until enough space is available */
+  if (log->last[logId] == &log->head) {
+    log->last[logId] = list_tail(&log->head);
+  }
+  while (log->size[logId] > log->maxSize[logId]) {
+    struct listnode *node = log->last[logId];
+    struct LogBufferElement *e;
+    struct android_log_logger_list *logger_list;
+
+    e = node_to_item(node, struct LogBufferElement, node);
+    log->number[logId]--;
+    log->size[logId] -= e->len;
+    logger_list_rdlock();
+    logger_list_for_each(logger_list) {
+      struct android_log_transport_context *transp;
+
+      transport_context_for_each(transp, logger_list) {
+        if ((transp->transport == &localLoggerRead) &&
+            (transp->context.node == node)) {
+          if (node == &log->head) {
+            transp->context.node = &log->head;
+          } else {
+            transp->context.node = node->next;
+          }
+        }
+      }
+    }
+    logger_list_unlock();
+    if (node != &log->head) {
+      log->last[logId] = node->prev;
+    }
+    list_remove(node);
+    free(e);
+  }
+  /* add entry to list */
+  list_add_head(&log->head, &element->node);
+  /* ToDo: wake up all readers */
+  pthread_rwlock_unlock(&log->listLock);
+
+  return element->len;
+}
+
+/*
+ * return zero if permitted to log directly to logd,
+ * return 1 if binder server started and
+ * return negative error number if failed to start binder server.
+ */
+static int writeToLocalInit() {
+  pthread_attr_t attr;
+  struct LogBuffer *log;
+
+  if (writeToLocalAvailable(LOG_ID_MAIN) < 0) {
+    return -EPERM;
+  }
+
+  log = &logbuf;
+  if (!log->serviceName) {
+      LogBufferInit(log);
+  }
+
+  if (!log->serviceName) {
+    LogBufferFree(log);
+    return -ENOMEM;
+  }
+
+  return EPERM; /* successful local-only logging */
+}
+
+static void writeToLocalReset() {
+  LogBufferFree(&logbuf);
+}
+
+static int writeToLocalAvailable(log_id_t logId) {
+#if !defined(__MINGW32__)
+  uid_t uid;
+#endif
+
+  if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) {
+    return -EINVAL;
+  }
+
+  /* Android hard coded permitted, system goes to logd */
+#if !defined(__MINGW32__)
+  if (__android_log_frontend == LOGGER_DEFAULT) {
+    uid = __android_log_uid();
+    if ((uid < AID_APP) && (getpwuid(uid) != NULL)) {
+      return -EPERM;
+    }
+  }
+#endif
+
+  /* ToDo: Ask package manager for LOGD permissions */
+  /* Assume we do _not_ have permissions to go to LOGD, so must go local */
+  return 0;
+}
+
+static int writeToLocalWrite(log_id_t logId, struct timespec *ts,
+                             struct iovec *vec, size_t nr) {
+  size_t len, i;
+  struct LogBufferElement *element;
+
+  if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) {
+    return -EINVAL;
+  }
+
+  len = 0;
+  for (i = 0; i < nr; ++i) {
+    len += vec[i].iov_len;
+  }
+
+  if (len > LOGGER_ENTRY_MAX_PAYLOAD) {
+    len = LOGGER_ENTRY_MAX_PAYLOAD;
+  }
+  element = (struct LogBufferElement *)calloc(1,
+      sizeof(struct LogBufferElement) + len + 1);
+  if (!element) {
+    return errno ? -errno : -ENOMEM;
+  }
+  element->timestamp.tv_sec = ts->tv_sec;
+  element->timestamp.tv_nsec = ts->tv_nsec;
+#ifdef __BIONIC__
+  element->tid = gettid();
+#else
+  element->tid = getpid();
+#endif
+  element->logId = logId;
+  element->len = len;
+
+  char *cp = element->msg;
+  for (i = 0; i < nr; ++i) {
+    size_t iov_len = vec[i].iov_len;
+    if (iov_len > len) {
+      iov_len = len;
+    }
+    memcpy(cp, vec[i].iov_base, iov_len);
+    len -= iov_len;
+    if (len == 0) {
+      break;
+    }
+    cp += iov_len;
+  }
+
+  return LogBufferLog(&logbuf, element);
+}
+
+static int writeToLocalVersion(
+        struct android_log_logger *logger __unused,
+        struct android_log_transport_context *transp __unused) {
+  return 3;
+}
+
+/* within reader lock, serviceName already validated */
+static struct listnode *writeToLocalNode(
+        struct android_log_logger_list *logger_list,
+        struct android_log_transport_context *transp) {
+  struct listnode *node;
+  unsigned logMask;
+  unsigned int tail;
+
+  node = transp->context.node;
+  if (node) {
+    return node;
+  }
+
+  if (!logger_list->tail) {
+    return transp->context.node = &logbuf.head;
+  }
+
+  logMask = transp->logMask;
+  tail = logger_list->tail;
+
+  for (node = list_head(&logbuf.head); node != &logbuf.head; node = node->next) {
+    struct LogBufferElement *element;
+    log_id_t logId;
+
+    element = node_to_item(node, struct LogBufferElement, node);
+    logId = element->logId;
+
+    if ((logMask & (1 << logId)) && !--tail) {
+      node = node->next;
+      break;
+    }
+  }
+  return transp->context.node = node;
+}
+
+static int writeToLocalRead(
+        struct android_log_logger_list *logger_list,
+        struct android_log_transport_context *transp,
+        struct log_msg *log_msg) {
+  int ret;
+  struct listnode *node;
+  unsigned logMask;
+
+  pthread_rwlock_rdlock(&logbuf.listLock);
+  if (!logbuf.serviceName) {
+    pthread_rwlock_unlock(&logbuf.listLock);
+    return (logger_list->mode & ANDROID_LOG_NONBLOCK) ? -ENODEV : 0;
+  }
+
+  logMask = transp->logMask;
+
+  node = writeToLocalNode(logger_list, transp);
+
+  ret = 0;
+
+  while (node != list_head(&logbuf.head)) {
+    struct LogBufferElement *element;
+    log_id_t logId;
+
+    node = node->prev;
+    element = node_to_item(node, struct LogBufferElement, node);
+    logId = element->logId;
+
+    if (logMask & (1 << logId)) {
+      ret = log_msg->entry_v3.len = element->len;
+      log_msg->entry_v3.hdr_size = sizeof(log_msg->entry_v3);
+      log_msg->entry_v3.pid = getpid();
+      log_msg->entry_v3.tid = element->tid;
+      log_msg->entry_v3.sec = element->timestamp.tv_sec;
+      log_msg->entry_v3.nsec = element->timestamp.tv_nsec;
+      log_msg->entry_v3.lid = logId;
+      memcpy(log_msg->entry_v3.msg, element->msg, ret);
+      ret += log_msg->entry_v3.hdr_size;
+      break;
+    }
+  }
+
+  transp->context.node = node;
+
+  /* ToDo: if blocking, and no entry, put reader to sleep */
+  pthread_rwlock_unlock(&logbuf.listLock);
+  return ret;
+}
+
+static int writeToLocalPoll(
+        struct android_log_logger_list *logger_list,
+        struct android_log_transport_context *transp) {
+  int ret = (logger_list->mode & ANDROID_LOG_NONBLOCK) ? -ENODEV : 0;
+
+  pthread_rwlock_rdlock(&logbuf.listLock);
+
+  if (logbuf.serviceName) {
+    unsigned logMask = transp->logMask;
+    struct listnode *node = writeToLocalNode(logger_list, transp);
+
+    ret = (node != list_head(&logbuf.head));
+    if (ret) {
+      do {
+        ret = !!(logMask & (1 << (node_to_item(node->prev,
+                                               struct LogBufferElement,
+                                               node))->logId));
+      } while (!ret && ((node = node->prev) != list_head(&logbuf.head)));
+    }
+
+    transp->context.node = node;
+  }
+
+  pthread_rwlock_unlock(&logbuf.listLock);
+
+  return ret;
+}
+
+static void writeToLocalClose(
+        struct android_log_logger_list *logger_list __unused,
+        struct android_log_transport_context *transp) {
+  pthread_rwlock_wrlock(&logbuf.listLock);
+  transp->context.node = list_head(&logbuf.head);
+  pthread_rwlock_unlock(&logbuf.listLock);
+}
+
+static int writeToLocalClear(
+        struct android_log_logger *logger,
+        struct android_log_transport_context *unused __unused) {
+  log_id_t logId = logger->logId;
+  struct listnode *node, *n;
+
+  if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) {
+    return -EINVAL;
+  }
+
+  pthread_rwlock_wrlock(&logbuf.listLock);
+  logbuf.number[logId] = 0;
+  logbuf.last[logId] = &logbuf.head;
+  list_for_each_safe(node, n, &logbuf.head) {
+    struct LogBufferElement *element;
+    element = node_to_item(node, struct LogBufferElement, node);
+
+    if (logId == element->logId) {
+      struct android_log_logger_list *logger_list;
+
+      logger_list_rdlock();
+      logger_list_for_each(logger_list) {
+        struct android_log_transport_context *transp;
+
+        transport_context_for_each(transp, logger_list) {
+          if ((transp->transport == &localLoggerRead) &&
+              (transp->context.node == node)) {
+            transp->context.node = node->next;
+          }
+        }
+      }
+      logger_list_unlock();
+      list_remove(node);
+      free(element);
+    }
+  }
+
+  pthread_rwlock_unlock(&logbuf.listLock);
+
+  return 0;
+}
+
+static ssize_t writeToLocalGetSize(
+        struct android_log_logger *logger,
+        struct android_log_transport_context *transp __unused) {
+  ssize_t ret = -EINVAL;
+  log_id_t logId = logger->logId;
+
+  if ((logId < NUMBER_OF_LOG_BUFFERS) && !BLOCK_LOG_BUFFERS(logId)) {
+    pthread_rwlock_rdlock(&logbuf.listLock);
+    ret = logbuf.maxSize[logId];
+    pthread_rwlock_unlock(&logbuf.listLock);
+  }
+
+  return ret;
+}
+
+static ssize_t writeToLocalSetSize(
+        struct android_log_logger *logger,
+        struct android_log_transport_context *transp __unused,
+        size_t size) {
+  ssize_t ret = -EINVAL;
+
+  if ((size > LOGGER_ENTRY_MAX_LEN) || (size < (4 * 1024 * 1024))) {
+    log_id_t logId = logger->logId;
+    if ((logId < NUMBER_OF_LOG_BUFFERS) || !BLOCK_LOG_BUFFERS(logId)) {
+      pthread_rwlock_wrlock(&logbuf.listLock);
+      ret = logbuf.maxSize[logId] = size;
+      pthread_rwlock_unlock(&logbuf.listLock);
+    }
+  }
+
+  return ret;
+}
+
+static ssize_t writeToLocalGetReadbleSize(
+        struct android_log_logger *logger,
+        struct android_log_transport_context *transp __unused) {
+  ssize_t ret = -EINVAL;
+  log_id_t logId = logger->logId;
+
+  if ((logId < NUMBER_OF_LOG_BUFFERS) && !BLOCK_LOG_BUFFERS(logId)) {
+    pthread_rwlock_rdlock(&logbuf.listLock);
+    ret = logbuf.serviceName ? (ssize_t)logbuf.size[logId] : -EBADF;
+    pthread_rwlock_unlock(&logbuf.listLock);
+  }
+
+  return ret;
+}
diff --git a/liblog/log_ratelimit.cpp b/liblog/log_ratelimit.cpp
new file mode 100644
index 0000000..dfd4b8f
--- /dev/null
+++ b/liblog/log_ratelimit.cpp
@@ -0,0 +1,86 @@
+/*
+** Copyright 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 <pthread.h>
+#include <time.h>
+
+#include <log/log.h>
+
+#include "log_portability.h"
+
+// Global default if 'last' argument in __android_log_ratelimit is NULL
+static time_t g_last_clock;
+// Global above can not deal well with callers playing games with the
+// seconds argument, so we will also hold on to the maximum value
+// ever provided and use that to gain consistency.  If the caller
+// provides their own 'last' argument, then they can play such games
+// of varying the 'seconds' argument to their pleasure.
+static time_t g_last_seconds;
+static const time_t last_seconds_default = 10;
+static const time_t last_seconds_max = 24 * 60 * 60; // maximum of a day
+static const time_t last_seconds_min = 2; // granularity
+// Lock to protect last_clock and last_seconds, but also 'last'
+// argument (not NULL) as supplied to __android_log_ratelimit.
+static pthread_mutex_t lock_ratelimit = PTHREAD_MUTEX_INITIALIZER;
+
+// if last is NULL, caller _must_ provide a consistent value for
+// seconds, otherwise we will take the maximum ever issued and hold
+// on to that.  Preserves value of non-zero errno.  Return -1 if we
+// can not acquire a lock, 0 if we are not to log a message, and 1
+// if we are ok to log a message.  Caller should check > 0 for true.
+LIBLOG_ABI_PUBLIC int __android_log_ratelimit(time_t seconds, time_t* last) {
+    int save_errno = errno;
+
+    // Two reasons for trylock failure:
+    //   1. In a signal handler. Must prevent deadlock
+    //   2. Too many threads calling __android_log_ratelimit.
+    //      Bonus to not print if they race here because that
+    //      dovetails the goal of ratelimiting. One may print
+    //      and the others will wait their turn ...
+    if (pthread_mutex_trylock(&lock_ratelimit)) {
+        if (save_errno) errno = save_errno;
+        return -1;
+    }
+
+    if (seconds == 0) {
+        seconds = last_seconds_default;
+    } else if (seconds < last_seconds_min) {
+        seconds = last_seconds_min;
+    } else if (seconds > last_seconds_max) {
+        seconds = last_seconds_max;
+    }
+
+    if (!last) {
+        if (g_last_seconds > seconds) {
+            seconds = g_last_seconds;
+        } else if (g_last_seconds < seconds) {
+            g_last_seconds = seconds;
+        }
+        last = &g_last_clock;
+    }
+
+    time_t now = time(NULL);
+    if ((now == (time_t)-1) || ((*last + seconds) > now)) {
+        pthread_mutex_unlock(&lock_ratelimit);
+        if (save_errno) errno = save_errno;
+        return 0;
+    }
+    *last = now;
+    pthread_mutex_unlock(&lock_ratelimit);
+    if (save_errno) errno = save_errno;
+    return 1;
+}
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.c
index 99d7fea..a6c3f7a 100644
--- a/liblog/logd_reader.c
+++ b/liblog/logd_reader.c
@@ -37,6 +37,7 @@
 
 #include "config_read.h"
 #include "log_portability.h"
+#include "logd_reader.h"
 #include "logger.h"
 
 /* branchless on many architectures. */
@@ -90,7 +91,7 @@
 
 static int logdAvailable(log_id_t logId)
 {
-    if (logId > LOG_ID_KERNEL) {
+    if (logId >= LOG_ID_MAX) {
         return -EINVAL;
     }
     if (logId == LOG_ID_SECURITY) {
@@ -324,6 +325,11 @@
     return ret;
 }
 
+LIBLOG_HIDDEN ssize_t __send_log_msg(char *buf, size_t buf_size)
+{
+    return send_log_msg(NULL, NULL, buf, buf_size);
+}
+
 static int check_log_success(char *buf, ssize_t ret)
 {
     if (ret < 0) {
diff --git a/bootstat/histogram_logger.h b/liblog/logd_reader.h
similarity index 70%
copy from bootstat/histogram_logger.h
copy to liblog/logd_reader.h
index 60c7776..04c2cf2 100644
--- a/bootstat/histogram_logger.h
+++ b/liblog/logd_reader.h
@@ -14,13 +14,17 @@
  * limitations under the License.
  */
 
-#include <cstdint>
-#include <string>
+#ifndef _LIBLOG_LOGD_READER_H__
+#define _LIBLOG_LOGD_READER_H__
 
-namespace bootstat {
+#include <unistd.h>
 
-// Builds an EventLog buffer named |event| containing |data| and writes
-// the log into the Tron histogram logs.
-void LogHistogram(const std::string& event, int32_t data);
+#include "log_portability.h"
 
-}  // namespace bootstat
\ No newline at end of file
+__BEGIN_DECLS
+
+LIBLOG_HIDDEN ssize_t __send_log_msg(char *buf, size_t buf_size);
+
+__END_DECLS
+
+#endif /* _LIBLOG_LOGD_READER_H__ */
diff --git a/liblog/logd_writer.c b/liblog/logd_writer.c
index 8fdfb92..12b797d 100644
--- a/liblog/logd_writer.c
+++ b/liblog/logd_writer.c
@@ -50,7 +50,7 @@
 
 LIBLOG_HIDDEN struct android_log_transport_write logdLoggerWrite = {
     .node = { &logdLoggerWrite.node, &logdLoggerWrite.node },
-    .context.sock = -1,
+    .context.sock = -EBADF,
     .name = "logd",
     .available = logdAvailable,
     .open = logdOpen,
@@ -65,8 +65,10 @@
 
     i = atomic_load(&logdLoggerWrite.context.sock);
     if (i < 0) {
-        i = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
-        if (i < 0) {
+        int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM |
+                                                      SOCK_CLOEXEC |
+                                                      SOCK_NONBLOCK, 0));
+        if (sock < 0) {
             ret = -errno;
         } else {
             struct sockaddr_un un;
@@ -74,13 +76,22 @@
             un.sun_family = AF_UNIX;
             strcpy(un.sun_path, "/dev/socket/logdw");
 
-            if (TEMP_FAILURE_RETRY(connect(i, (struct sockaddr *)&un,
+            if (TEMP_FAILURE_RETRY(connect(sock, (struct sockaddr *)&un,
                                            sizeof(struct sockaddr_un))) < 0) {
                 ret = -errno;
-                close(i);
+                switch (ret) {
+                case -ENOTCONN:
+                case -ECONNREFUSED:
+                case -ENOENT:
+                    i = atomic_exchange(&logdLoggerWrite.context.sock, ret);
+                    /* FALLTHRU */
+                default:
+                    break;
+                }
+                close(sock);
             } else {
-                ret = atomic_exchange(&logdLoggerWrite.context.sock, i);
-                if ((ret >= 0) && (ret != i)) {
+                ret = atomic_exchange(&logdLoggerWrite.context.sock, sock);
+                if ((ret >= 0) && (ret != sock)) {
                     close(ret);
                 }
                 ret = 0;
@@ -91,17 +102,22 @@
     return ret;
 }
 
-static void logdClose()
+static void __logdClose(int negative_errno)
 {
-    int sock = atomic_exchange(&logdLoggerWrite.context.sock, -1);
+    int sock = atomic_exchange(&logdLoggerWrite.context.sock, negative_errno);
     if (sock >= 0) {
         close(sock);
     }
 }
 
+static void logdClose()
+{
+    __logdClose(-EBADF);
+}
+
 static int logdAvailable(log_id_t logId)
 {
-    if (logId > LOG_ID_SECURITY) {
+    if (logId >= LOG_ID_MAX || logId == LOG_ID_KERNEL) {
         return -EINVAL;
     }
     if (atomic_load(&logdLoggerWrite.context.sock) < 0) {
@@ -117,6 +133,7 @@
                      struct iovec *vec, size_t nr)
 {
     ssize_t ret;
+    int sock;
     static const unsigned headerLength = 1;
     struct iovec newVec[nr + headerLength];
     android_log_header_t header;
@@ -124,7 +141,13 @@
     static atomic_int_fast32_t dropped;
     static atomic_int_fast32_t droppedSecurity;
 
-    if (atomic_load(&logdLoggerWrite.context.sock) < 0) {
+    sock = atomic_load(&logdLoggerWrite.context.sock);
+    if (sock < 0) switch (sock) {
+    case -ENOTCONN:
+    case -ECONNREFUSED:
+    case -ENOENT:
+        break;
+    default:
         return -EBADF;
     }
 
@@ -163,7 +186,7 @@
     newVec[0].iov_base = (unsigned char *)&header;
     newVec[0].iov_len  = sizeof(header);
 
-    if (atomic_load(&logdLoggerWrite.context.sock) > 0) {
+    if (sock >= 0) {
         int32_t snapshot = atomic_exchange_explicit(&droppedSecurity, 0,
                                                     memory_order_relaxed);
         if (snapshot) {
@@ -177,8 +200,7 @@
             newVec[headerLength].iov_base = &buffer;
             newVec[headerLength].iov_len  = sizeof(buffer);
 
-            ret = TEMP_FAILURE_RETRY(writev(
-                    atomic_load(&logdLoggerWrite.context.sock), newVec, 2));
+            ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
             if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
                 atomic_fetch_add_explicit(&droppedSecurity, snapshot,
                                           memory_order_relaxed);
@@ -186,7 +208,8 @@
         }
         snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
         if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO,
-                                                      "liblog", strlen("liblog"),
+                                                      "liblog",
+                                                      strlen("liblog"),
                                                       ANDROID_LOG_VERBOSE)) {
             android_log_event_int_t buffer;
 
@@ -198,8 +221,7 @@
             newVec[headerLength].iov_base = &buffer;
             newVec[headerLength].iov_len  = sizeof(buffer);
 
-            ret = TEMP_FAILURE_RETRY(writev(
-                      atomic_load(&logdLoggerWrite.context.sock), newVec, 2));
+            ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
             if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
                 atomic_fetch_add_explicit(&dropped, snapshot,
                                           memory_order_relaxed);
@@ -225,30 +247,43 @@
     /*
      * The write below could be lost, but will never block.
      *
-     * ENOTCONN occurs if logd dies.
+     * ENOTCONN occurs if logd has died.
+     * ENOENT occurs if logd is not running and socket is missing.
+     * ECONNREFUSED occurs if we can not reconnect to logd.
      * EAGAIN occurs if logd is overloaded.
      */
-    ret = TEMP_FAILURE_RETRY(writev(
-            atomic_load(&logdLoggerWrite.context.sock), newVec, i));
-    if (ret < 0) {
-        ret = -errno;
-        if (ret == -ENOTCONN) {
-            __android_log_lock();
-            logdClose();
-            ret = logdOpen();
-            __android_log_unlock();
-
-            if (ret < 0) {
-                return ret;
-            }
-
-            ret = TEMP_FAILURE_RETRY(writev(
-                    atomic_load(&logdLoggerWrite.context.sock), newVec, i));
-            if (ret < 0) {
-                ret = -errno;
-            }
+    if (sock < 0) {
+        ret = sock;
+    } else {
+        ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i));
+        if (ret < 0) {
+            ret = -errno;
         }
     }
+    switch(ret) {
+    case -ENOTCONN:
+    case -ECONNREFUSED:
+    case -ENOENT:
+        if (__android_log_trylock()) {
+            return ret; /* in a signal handler? try again when less stressed */
+        }
+        __logdClose(ret);
+        ret = logdOpen();
+        __android_log_unlock();
+
+        if (ret < 0) {
+            return ret;
+        }
+
+        ret = TEMP_FAILURE_RETRY(writev(
+                atomic_load(&logdLoggerWrite.context.sock), newVec, i));
+        if (ret < 0) {
+            ret = -errno;
+        }
+        /* FALLTHRU */
+    default:
+        break;
+    }
 
     if (ret > (ssize_t)sizeof(header)) {
         ret -= sizeof(header);
diff --git a/liblog/logger.h b/liblog/logger.h
index 50d1cb4..d94cd14 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -39,14 +39,16 @@
 
 struct android_log_transport_write {
   struct listnode node;
-  const char *name;
-  unsigned logMask; /* cache of available success */
+  const char *name; /* human name to describe the transport */
+  unsigned logMask; /* mask cache of available() success */
   union android_log_context context; /* Initialized by static allocation */
 
-  int (*available)(log_id_t logId);
-  int (*open)();
-  void (*close)();
-  int (*write)(log_id_t logId, struct timespec *ts, struct iovec *vec, size_t nr);
+  int (*available)(log_id_t logId); /* Does not cause resources to be taken */
+  int (*open)();   /* can be called multiple times, reusing current resources */
+  void (*close)(); /* free up resources */
+  /* write log to transport, returns number of bytes propagated, or -errno */
+  int (*write)(log_id_t logId, struct timespec *ts,
+               struct iovec *vec, size_t nr);
 };
 
 struct android_log_logger_list;
@@ -55,22 +57,23 @@
 
 struct android_log_transport_read {
   struct listnode node;
-  const char *name;
+  const char *name; /* human name to describe the transport */
 
+  /* Does not cause resources to be taken */
   int (*available)(log_id_t logId);
   int (*version)(struct android_log_logger *logger,
                  struct android_log_transport_context *transp);
+  /* Release resources taken by the following interfaces */
   void (*close)(struct android_log_logger_list *logger_list,
                 struct android_log_transport_context *transp);
-
   /*
-   * Expect all to instantiate open on any call, so we do not have
-   * an expicit open call
+   * Expect all to instantiate open automagically on any call,
+   * so we do not have an explicit open call.
    */
   int (*read)(struct android_log_logger_list *logger_list,
               struct android_log_transport_context *transp,
               struct log_msg *log_msg);
-  /* Assumption is only called if not ANDROID_LOG_NONBLOCK */
+  /* Must only be called if not ANDROID_LOG_NONBLOCK (blocking) */
   int (*poll)(struct android_log_logger_list *logger_list,
               struct android_log_transport_context *transp);
 
@@ -96,6 +99,7 @@
 };
 
 struct android_log_logger_list {
+  struct listnode node;
   struct listnode logger;
   struct listnode transport;
   int mode;
@@ -117,9 +121,9 @@
   struct android_log_logger_list *parent;
 
   struct android_log_transport_read *transport;
-  unsigned logMask;
-  int ret;
-  struct log_msg logMsg; /* valid is logMsg.len != 0 */
+  unsigned logMask;      /* mask of requested log buffers */
+  int ret;               /* return value associated with following data */
+  struct log_msg logMsg; /* peek at upcoming data, valid if logMsg.len != 0 */
 };
 
 /* assumes caller has structures read-locked, single threaded, or fenced */
@@ -143,6 +147,41 @@
          (logp) = node_to_item((logp)->node.next,                   \
                              struct android_log_logger, node))
 
+/*
+ *    Global list of log readers.
+ *
+ * Usage case: search out transport contexts for all readers
+ */
+
+LIBLOG_HIDDEN struct listnode __android_log_readers;
+
+#if defined(_WIN32)
+#define logger_list_rdlock()
+#define logger_list_wrlock()
+#define logger_list_unlock()
+#else
+LIBLOG_HIDDEN pthread_rwlock_t __android_log_readers_lock;
+
+#define logger_list_rdlock() pthread_rwlock_rdlock(&__android_log_readers_lock)
+#define logger_list_wrlock() pthread_rwlock_wrlock(&__android_log_readers_lock)
+#define logger_list_unlock() pthread_rwlock_unlock(&__android_log_readers_lock)
+#endif
+
+/* Must be called with logger_list_rdlock() or logger_list_wrlock() held */
+#define logger_list_for_each(logger_list)                              \
+    for ((logger_list) = node_to_item(&__android_log_readers,          \
+                                      struct android_log_logger_list,  \
+                                      node);                           \
+         (logger_list) != node_to_item(&__android_log_readers,         \
+                                       struct android_log_logger_list, \
+                                       node) &&                        \
+         (logger_list) != node_to_item((logger_list)->node.next,       \
+                                       struct android_log_logger_list, \
+                                       node);                          \
+         (logger_list) = node_to_item((logger_list)->node.next,        \
+                                      struct android_log_logger_list,  \
+                                      node))
+
 /* OS specific dribs and drabs */
 
 #if defined(_WIN32)
@@ -157,6 +196,8 @@
 LIBLOG_HIDDEN int __android_log_trylock();
 LIBLOG_HIDDEN void __android_log_unlock();
 
+LIBLOG_HIDDEN int __android_log_frontend;
+
 __END_DECLS
 
 #endif /* _LIBLOG_LOGGER_H__ */
diff --git a/liblog/logger_read.c b/liblog/logger_read.c
index c3cb7ad..7e50a23 100644
--- a/liblog/logger_read.c
+++ b/liblog/logger_read.c
@@ -228,6 +228,13 @@
     LOGGER_LIST_FUNCTION(logger_list, -ENODEV, setPrune, buf, len);
 }
 
+LIBLOG_HIDDEN struct listnode __android_log_readers =
+    { &__android_log_readers, &__android_log_readers };
+#if !defined(_WIN32)
+LIBLOG_HIDDEN pthread_rwlock_t __android_log_readers_lock =
+    PTHREAD_RWLOCK_INITIALIZER;
+#endif
+
 LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_alloc(
         int mode,
         unsigned int tail,
@@ -246,6 +253,10 @@
     logger_list->tail = tail;
     logger_list->pid = pid;
 
+    logger_list_wrlock();
+    list_add_tail(&__android_log_readers, &logger_list->node);
+    logger_list_unlock();
+
     return (struct logger_list *)logger_list;
 }
 
@@ -267,6 +278,10 @@
     logger_list->start = start;
     logger_list->pid = pid;
 
+    logger_list_wrlock();
+    list_add_tail(&__android_log_readers, &logger_list->node);
+    logger_list_unlock();
+
     return (struct logger_list *)logger_list;
 }
 
@@ -502,6 +517,10 @@
         return;
     }
 
+    logger_list_wrlock();
+    list_remove(&logger_list_internal->node);
+    logger_list_unlock();
+
     while (!list_empty(&logger_list_internal->transport)) {
         struct listnode *node = list_head(&logger_list_internal->transport);
         struct android_log_transport_context *transp =
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index f19c3ab..e149e68 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -25,9 +25,11 @@
 #endif
 
 #include <log/event_tag_map.h>
+#include <log/log_frontend.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
+#include "config_read.h" /* __android_log_config_read_close() definition */
 #include "config_write.h"
 #include "log_portability.h"
 #include "logger.h"
@@ -170,6 +172,8 @@
         }
     }
 
+    __android_log_config_write_close();
+
 #if defined(__ANDROID__)
     /*
      * Additional risk here somewhat mitigated by immediately unlock flushing
@@ -262,6 +266,8 @@
     }
 
 #if defined(__ANDROID__)
+    clock_gettime(android_log_clockid(), &ts);
+
     if (log_id == LOG_ID_SECURITY) {
         if (vec[0].iov_len < 4) {
             return -EINVAL;
@@ -341,7 +347,7 @@
             }
         }
         /* tag must be nul terminated */
-        if (strnlen(tag, len) >= len) {
+        if (tag && strnlen(tag, len) >= len) {
             tag = NULL;
         }
 
@@ -351,8 +357,6 @@
             return -EPERM;
         }
     }
-
-    clock_gettime(android_log_clockid(), &ts);
 #else
     /* simulate clock_gettime(CLOCK_REALTIME, &ts); */
     {
@@ -514,6 +518,14 @@
             strcpy(buf, "Unspecified assertion failed");
     }
 
+    // Log assertion failures to stderr for the benefit of "adb shell" users
+    // and gtests (http://b/23675822).
+    struct iovec iov[2] = {
+        { buf, strlen(buf) },
+        { (char*) "\n", 1 },
+    };
+    TEMP_FAILURE_RETRY(writev(2, iov, 2));
+
     __android_log_write(ANDROID_LOG_FATAL, tag, buf);
     abort(); /* abort so we have a chance to debug the situation */
     /* NOTREACHED */
@@ -610,3 +622,87 @@
 
     return write_to_log(LOG_ID_SECURITY, vec, 4);
 }
+
+static int __write_to_log_null(log_id_t log_id, struct iovec* vec, size_t nr)
+{
+    size_t len, i;
+
+    if ((log_id < LOG_ID_MIN) || (log_id >= LOG_ID_MAX)) {
+        return -EINVAL;
+    }
+
+    for (len = i = 0; i < nr; ++i) {
+        len += vec[i].iov_len;
+    }
+    if (!len) {
+        return -EINVAL;
+    }
+    return len;
+}
+
+/* Following functions need access to our internal write_to_log status */
+
+LIBLOG_HIDDEN int __android_log_frontend;
+
+LIBLOG_ABI_PUBLIC int android_set_log_frontend(int frontend_flag)
+{
+    int retval;
+
+    if (frontend_flag < 0) {
+        return -EINVAL;
+    }
+
+    retval = LOGGER_NULL;
+
+    __android_log_lock();
+
+    if (frontend_flag & LOGGER_NULL) {
+        write_to_log = __write_to_log_null;
+
+        __android_log_unlock();
+
+        return retval;
+    }
+
+    __android_log_frontend &= LOGGER_LOCAL | LOGGER_LOGD;
+
+    frontend_flag &= LOGGER_LOCAL | LOGGER_LOGD;
+
+    if (__android_log_frontend != frontend_flag) {
+        __android_log_frontend = frontend_flag;
+        __android_log_config_write_close();
+        __android_log_config_read_close();
+
+        write_to_log = __write_to_log_init;
+    /* generically we only expect these two values for write_to_log */
+    } else if ((write_to_log != __write_to_log_init) &&
+               (write_to_log != __write_to_log_daemon)) {
+        write_to_log = __write_to_log_init;
+    }
+
+    retval = __android_log_frontend;
+
+    __android_log_unlock();
+
+    return retval;
+}
+
+LIBLOG_ABI_PUBLIC int android_get_log_frontend()
+{
+    int ret = LOGGER_DEFAULT;
+
+    __android_log_lock();
+    if (write_to_log == __write_to_log_null) {
+        ret = LOGGER_NULL;
+    } else {
+        __android_log_frontend &= LOGGER_LOCAL | LOGGER_LOGD;
+        ret = __android_log_frontend;
+        if ((write_to_log != __write_to_log_init) &&
+            (write_to_log != __write_to_log_daemon)) {
+            ret = -EINVAL;
+        }
+    }
+    __android_log_unlock();
+
+    return ret;
+}
diff --git a/liblog/logprint.c b/liblog/logprint.c
index da80e36..4421f83 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -16,13 +16,17 @@
 */
 
 #define _GNU_SOURCE /* for asprintf */
+#ifndef __MINGW32__
+#define HAVE_STRSEP
+#endif
 
-#include <arpa/inet.h>
 #include <assert.h>
 #include <ctype.h>
 #include <errno.h>
 #include <inttypes.h>
+#ifndef __MINGW32__
 #include <pwd.h>
+#endif
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -40,6 +44,10 @@
 #define MS_PER_NSEC 1000000
 #define US_PER_NSEC 1000
 
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
 typedef struct FilterInfo_t {
     char *mTag;
     android_LogPriority mPri;
@@ -52,6 +60,7 @@
     AndroidLogPrintFormat format;
     bool colored_output;
     bool usec_time_output;
+    bool nsec_time_output;
     bool printable_output;
     bool year_output;
     bool zone_output;
@@ -105,7 +114,7 @@
     c = tolower(c);
 
     if (c >= '0' && c <= '9') {
-        if (c >= ('0'+ANDROID_LOG_SILENT)) {
+        if (c >= ('0' + ANDROID_LOG_SILENT)) {
             pri = ANDROID_LOG_VERBOSE;
         } else {
             pri = (android_LogPriority)(c - '0');
@@ -210,11 +219,16 @@
     p_ret->format = FORMAT_BRIEF;
     p_ret->colored_output = false;
     p_ret->usec_time_output = false;
+    p_ret->nsec_time_output = false;
     p_ret->printable_output = false;
     p_ret->year_output = false;
     p_ret->zone_output = false;
     p_ret->epoch_output = false;
+#ifdef __ANDROID__
     p_ret->monotonic_output = android_log_clockid() == CLOCK_MONOTONIC;
+#else
+    p_ret->monotonic_output = false;
+#endif
     p_ret->uid_output = false;
     p_ret->descriptive_output = false;
     descriptive_output = false;
@@ -258,6 +272,9 @@
     case FORMAT_MODIFIER_TIME_USEC:
         p_format->usec_time_output = true;
         return 0;
+    case FORMAT_MODIFIER_TIME_NSEC:
+        p_format->nsec_time_output = true;
+        return 0;
     case FORMAT_MODIFIER_PRINTABLE:
         p_format->printable_output = true;
         return 0;
@@ -309,6 +326,7 @@
     else if (strcmp(formatString, "color") == 0) format = FORMAT_MODIFIER_COLOR;
     else if (strcmp(formatString, "colour") == 0) format = FORMAT_MODIFIER_COLOR;
     else if (strcmp(formatString, "usec") == 0) format = FORMAT_MODIFIER_TIME_USEC;
+    else if (strcmp(formatString, "nsec") == 0) format = FORMAT_MODIFIER_TIME_NSEC;
     else if (strcmp(formatString, "printable") == 0) format = FORMAT_MODIFIER_PRINTABLE;
     else if (strcmp(formatString, "year") == 0) format = FORMAT_MODIFIER_YEAR;
     else if (strcmp(formatString, "zone") == 0) format = FORMAT_MODIFIER_ZONE;
@@ -316,6 +334,7 @@
     else if (strcmp(formatString, "monotonic") == 0) format = FORMAT_MODIFIER_MONOTONIC;
     else if (strcmp(formatString, "uid") == 0) format = FORMAT_MODIFIER_UID;
     else if (strcmp(formatString, "descriptive") == 0) format = FORMAT_MODIFIER_DESCRIPT;
+#ifndef __MINGW32__
     else {
         extern char *tzname[2];
         static const char gmt[] = "GMT";
@@ -347,6 +366,7 @@
         }
         free(cp);
     }
+#endif
 
     return format;
 }
@@ -374,7 +394,7 @@
     }
 
     if(filterExpression[tagNameLength] == ':') {
-        pri = filterCharToPri(filterExpression[tagNameLength+1]);
+        pri = filterCharToPri(filterExpression[tagNameLength + 1]);
 
         if (pri == ANDROID_LOG_UNKNOWN) {
             goto error;
@@ -405,7 +425,7 @@
 
 /*
  * Presently HAVE_STRNDUP is never defined, so the second case is always taken
- * Darwin doesn't have strnup, everything else does
+ * Darwin doesn't have strndup, everything else does
  */
 #ifdef HAVE_STRNDUP
         tagName = strndup(filterExpression, tagNameLength);
@@ -427,6 +447,27 @@
     return -1;
 }
 
+#ifndef HAVE_STRSEP
+/* KISS replacement helper for below */
+static char* strsep(char** stringp, const char* delim)
+{
+    char* token;
+    char* ret = *stringp;
+
+    if (!ret || !*ret) {
+        return NULL;
+    }
+    token = strpbrk(ret, delim);
+    if (token) {
+        *token = '\0';
+        ++token;
+    } else {
+        token = ret + strlen(ret);
+    }
+    *stringp = token;
+    return ret;
+}
+#endif
 
 /**
  * filterString: a comma/whitespace-separated set of filter expressions
@@ -438,7 +479,6 @@
  * Assumes single threaded execution
  *
  */
-
 LIBLOG_ABI_PUBLIC int android_log_addFilterString(
         AndroidLogFormat *p_format,
         const char *filterString)
@@ -478,6 +518,9 @@
         struct logger_entry *buf,
         AndroidLogEntry *entry)
 {
+    entry->message = NULL;
+    entry->messageLen = 0;
+
     entry->tv_sec = buf->sec;
     entry->tv_nsec = buf->nsec;
     entry->uid = -1;
@@ -578,18 +621,18 @@
 
     low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
     high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
-    return ((uint64_t) high << 32) | (uint64_t) low;
+    return ((uint64_t)high << 32) | (uint64_t)low;
 }
 
 static bool findChar(const char** cp, size_t* len, int c) {
-    while (*len && isspace(**cp)) {
-        ++*cp;
-        --*len;
+    while ((*len) && isspace(*(*cp))) {
+        ++(*cp);
+        --(*len);
     }
     if (c == INT_MAX) return *len;
-    if (*len && (**cp == c)) {
-        ++*cp;
-        --*len;
+    if ((*len) && (*(*cp) == c)) {
+        ++(*cp);
+        --(*len);
         return true;
     }
     return false;
@@ -722,6 +765,7 @@
             }
         }
     }
+    outCount = 0;
     lval = 0;
     switch (type) {
     case EVENT_TYPE_INT:
@@ -785,7 +829,10 @@
             eventData += 4;
             eventDataLen -= 4;
 
-            if (eventDataLen < strLen) return -1;
+            if (eventDataLen < strLen) {
+                result = -1; /* mark truncated */
+                strLen = eventDataLen;
+            }
 
             if (cp && (strLen == 0)) {
                 /* reset the format if no content */
@@ -796,15 +843,18 @@
                 memcpy(outBuf, eventData, strLen);
                 outBuf += strLen;
                 outBufLen -= strLen;
-            } else if (outBufLen > 0) {
-                /* copy what we can */
-                memcpy(outBuf, eventData, outBufLen);
-                outBuf += outBufLen;
-                outBufLen -= outBufLen;
-                goto no_room;
+            } else {
+                if (outBufLen > 0) {
+                    /* copy what we can */
+                    memcpy(outBuf, eventData, outBufLen);
+                    outBuf += outBufLen;
+                    outBufLen -= outBufLen;
+                }
+                if (!result) result = 1; /* if not truncated, return no room */
             }
             eventData += strLen;
             eventDataLen -= strLen;
+            if (result != 0) goto bail;
             break;
         }
     case EVENT_TYPE_LIST:
@@ -947,13 +997,16 @@
 LIBLOG_ABI_PUBLIC int android_log_processBinaryLogBuffer(
         struct logger_entry *buf,
         AndroidLogEntry *entry,
-        const EventTagMap *map,
+        const EventTagMap *map __unused, /* only on !__ANDROID__ */
         char *messageBuf, int messageBufLen)
 {
     size_t inCount;
     uint32_t tagIndex;
     const unsigned char* eventData;
 
+    entry->message = NULL;
+    entry->messageLen = 0;
+
     entry->tv_sec = buf->sec;
     entry->tv_nsec = buf->nsec;
     entry->priority = ANDROID_LOG_INFO;
@@ -965,7 +1018,7 @@
      * Pull the tag out, fill in some additional details based on incoming
      * buffer version (v3 adds lid, v4 adds uid).
      */
-    eventData = (const unsigned char*) buf->msg;
+    eventData = (const unsigned char*)buf->msg;
     struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
     if (buf2->hdr_size) {
         if ((buf2->hdr_size < sizeof(((struct log_msg *)NULL)->entry_v1)) ||
@@ -989,11 +1042,12 @@
     inCount -= 4;
 
     entry->tagLen = 0;
+    entry->tag = NULL;
+#ifdef __ANDROID__
     if (map != NULL) {
         entry->tag = android_lookupEventTag_len(map, &entry->tagLen, tagIndex);
-    } else {
-        entry->tag = NULL;
     }
+#endif
 
     /*
      * If we don't have a map, or didn't find the tag number in the map,
@@ -1018,9 +1072,11 @@
      */
     const char* fmtStr = NULL;
     size_t fmtLen = 0;
+#ifdef __ANDROID__
     if (descriptive_output && map) {
         fmtStr = android_lookupEventFormat_len(map, &fmtLen, tagIndex);
     }
+#endif
 
     char* outBuf = messageBuf;
     size_t outRemaining = messageBufLen - 1; /* leave one for nul byte */
@@ -1075,7 +1131,7 @@
      */
     *outBuf = '\0';
     entry->messageLen = outBuf - messageBuf;
-    assert(entry->messageLen == (messageBufLen-1) - outRemaining);
+    assert(entry->messageLen == (messageBufLen - 1) - outRemaining);
 
     entry->message = messageBuf;
 
@@ -1170,7 +1226,7 @@
                 } else if (*message == '\b') {
                     strcpy(buf, "\\b");
                 } else if (*message == '\t') {
-                    strcpy(buf, "\t"); // Do not escape tabs
+                    strcpy(buf, "\t"); /* Do not escape tabs */
                 } else if (*message == '\v') {
                     strcpy(buf, "\\v");
                 } else if (*message == '\f') {
@@ -1244,6 +1300,7 @@
     return (long long)now->tv_sec * NS_PER_SEC + now->tv_nsec;
 }
 
+#ifdef __ANDROID__
 static void convertMonotonic(struct timespec *result,
                              const AndroidLogEntry *entry)
 {
@@ -1476,6 +1533,7 @@
     result->tv_nsec = entry->tv_nsec;
     subTimespec(result, result, &convert);
 }
+#endif
 
 /**
  * Formats a log message into a buffer
@@ -1496,7 +1554,8 @@
     struct tm tmBuf;
 #endif
     struct tm* ptm;
-    char timeBuf[64]; /* good margin, 23+nul for msec, 26+nul for usec */
+    /* good margin, 23+nul for msec, 26+nul for usec, 29+nul to nsec */
+    char timeBuf[64];
     char prefixBuf[128], suffixBuf[128];
     char priChar;
     int prefixSuffixIsHeaderFooter = 0;
@@ -1522,8 +1581,9 @@
      */
     now = entry->tv_sec;
     nsec = entry->tv_nsec;
+#if __ANDROID__
     if (p_format->monotonic_output) {
-        // prevent convertMonotonic from being called if logd is monotonic
+        /* prevent convertMonotonic from being called if logd is monotonic */
         if (android_log_clockid() != CLOCK_MONOTONIC) {
             struct timespec time;
             convertMonotonic(&time, entry);
@@ -1531,6 +1591,7 @@
             nsec = time.tv_nsec;
         }
     }
+#endif
     if (now < 0) {
         nsec = NS_PER_SEC - nsec;
     }
@@ -1550,7 +1611,10 @@
                  ptm);
     }
     len = strlen(timeBuf);
-    if (p_format->usec_time_output) {
+    if (p_format->nsec_time_output) {
+        len += snprintf(timeBuf + len, sizeof(timeBuf) - len,
+                        ".%09ld", nsec);
+    } else if (p_format->usec_time_output) {
         len += snprintf(timeBuf + len, sizeof(timeBuf) - len,
                         ".%06ld", nsec / US_PER_NSEC);
     } else {
@@ -1581,14 +1645,19 @@
              * This code is Android specific, bionic guarantees that
              * calls to non-reentrant getpwuid() are thread safe.
              */
+#if !defined(__MINGW32__)
+#if (FAKE_LOG_DEVICE == 0)
 #ifndef __BIONIC__
 #warning "This code assumes that getpwuid is thread safe, only true with Bionic!"
 #endif
+#endif
             struct passwd* pwd = getpwuid(entry->uid);
             if (pwd && (strlen(pwd->pw_name) <= 5)) {
                  snprintf(uid, sizeof(uid), "%5s:", pwd->pw_name);
-            } else {
-                 // Not worth parsing package list, names all longer than 5
+            } else
+#endif
+            {
+                 /* Not worth parsing package list, names all longer than 5 */
                  snprintf(uid, sizeof(uid), "%5d:", entry->uid);
             }
         } else {
@@ -1698,7 +1767,7 @@
             if (*pm++ == '\n') numLines++;
         }
         /* plus one line for anything not newline-terminated at the end */
-        if (pm > entry->message && *(pm-1) != '\n') numLines++;
+        if (pm > entry->message && *(pm - 1) != '\n') numLines++;
     }
 
     /*
diff --git a/liblog/pmsg_reader.c b/liblog/pmsg_reader.c
index a0a69c1..e1b81aa 100644
--- a/liblog/pmsg_reader.c
+++ b/liblog/pmsg_reader.c
@@ -70,7 +70,7 @@
 /* Determine the credentials of the caller */
 static bool uid_has_log_permission(uid_t uid)
 {
-    return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT);
+    return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT) || (uid == AID_LOGD);
 }
 
 static uid_t get_best_effective_uid()
diff --git a/liblog/pmsg_writer.c b/liblog/pmsg_writer.c
index c1c068e..5e4ff98 100644
--- a/liblog/pmsg_writer.c
+++ b/liblog/pmsg_writer.c
@@ -285,6 +285,7 @@
                 __android_log_unlock();
             } else if (pmsgOpen() < 0) {
                 __android_log_unlock();
+                free(cp);
                 return -EBADF;
             }
         }
diff --git a/liblog/log_is_loggable.c b/liblog/properties.c
similarity index 100%
rename from liblog/log_is_loggable.c
rename to liblog/properties.c
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
index ec5a99b..cfea452 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -55,7 +55,13 @@
     -fno-builtin \
 
 test_src_files := \
-    liblog_test.cpp
+    liblog_test_default.cpp \
+    liblog_test_local.cpp \
+    log_id_test.cpp \
+    log_radio_test.cpp \
+    log_read_test.cpp \
+    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),)
@@ -106,6 +112,7 @@
 LOCAL_CXX_STL := libc++
 LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_LDLIBS_linux := -lrt
 include $(BUILD_HOST_NATIVE_TEST)
 
 endif  # ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 5420f68..dac84eb 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -15,6 +15,8 @@
  */
 
 #include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
 #include <sys/endian.h>
 #include <sys/socket.h>
 #include <sys/types.h>
@@ -22,8 +24,10 @@
 
 #include <unordered_set>
 
+#include <android-base/file.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
+#include <log/log_frontend.h>
 #include <private/android_logger.h>
 
 #include "benchmark.h"
@@ -77,10 +81,29 @@
 }
 BENCHMARK(BM_log_maximum);
 
+static void set_log_null() {
+    android_set_log_frontend(LOGGER_NULL);
+}
+
+static void set_log_default() {
+    android_set_log_frontend(LOGGER_DEFAULT);
+}
+
+static void BM_log_maximum_null(int iters) {
+    set_log_null();
+    BM_log_maximum(iters);
+    set_log_default();
+}
+BENCHMARK(BM_log_maximum_null);
+
 /*
- *	Measure the time it takes to submit the android logging call using
- * discrete acquisition under light load. Expect this to be a pair of
- * syscall periods (2us).
+ *	Measure the time it takes to collect the time using
+ * discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
+ * under light load. Expect this to be a syscall period (2us) or
+ * data read time if zero-syscall.
+ *
+ * vdso support in the kernel and the library can allow
+ * clock_gettime to be zero-syscall.
  */
 static void BM_clock_overhead(int iters) {
     for (int i = 0; i < iters; ++i) {
@@ -465,19 +488,94 @@
 BENCHMARK(BM_pmsg_long_unaligned1);
 
 /*
- *	Measure the time it takes to submit the android logging call using
- * discrete acquisition under light load. Expect this to be a dozen or so
- * syscall periods (40us).
+ *	Measure the time it takes to form sprintf plus time using
+ * discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
+ * under light load. Expect this to be a syscall period (2us) or sprintf
+ * time if zero-syscall time.
  */
-static void BM_log_overhead(int iters) {
+/* helper function */
+static void test_print(const char *fmt, ...) {
+    va_list ap;
+    char buf[1024];
+
+    va_start(ap, fmt);
+    vsnprintf(buf, sizeof(buf), fmt, ap);
+    va_end(ap);
+}
+
+#define logd_yield() sched_yield() // allow logd to catch up
+#define logd_sleep() usleep(50)    // really allow logd to catch up
+
+/* performance test */
+static void BM_sprintf_overhead(int iters) {
+    for (int i = 0; i < iters; ++i) {
+       StartBenchmarkTiming();
+       test_print("BM_sprintf_overhead:%d", i);
+       StopBenchmarkTiming();
+       logd_yield();
+    }
+}
+BENCHMARK(BM_sprintf_overhead);
+
+/*
+ *	Measure the time it takes to submit the android printing logging call
+ * using discrete acquisition discrete acquisition (StartBenchmarkTiming() ->
+ * StopBenchmarkTiming()) under light load. Expect this to be a dozen or so
+ * syscall periods (40us) plus time to run *printf
+ */
+static void BM_log_print_overhead(int iters) {
     for (int i = 0; i < iters; ++i) {
        StartBenchmarkTiming();
        __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%d", i);
        StopBenchmarkTiming();
-       usleep(1000);
+       logd_yield();
     }
 }
-BENCHMARK(BM_log_overhead);
+BENCHMARK(BM_log_print_overhead);
+
+/*
+ *	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)
+ */
+static void BM_log_event_overhead(int iters) {
+    for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
+       StartBenchmarkTiming();
+       __android_log_btwrite(0, EVENT_TYPE_LONG, &i, sizeof(i));
+       StopBenchmarkTiming();
+       logd_yield();
+    }
+}
+BENCHMARK(BM_log_event_overhead);
+
+static void BM_log_event_overhead_null(int iters) {
+    set_log_null();
+    BM_log_event_overhead(iters);
+    set_log_default();
+}
+BENCHMARK(BM_log_event_overhead_null);
+
+/*
+ *	Measure the time it takes to submit the android event logging call
+ * using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
+ * under very-light load (<1% CPU utilization).
+ */
+static void BM_log_light_overhead(int iters) {
+    for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
+       StartBenchmarkTiming();
+       __android_log_btwrite(0, EVENT_TYPE_LONG, &i, sizeof(i));
+       StopBenchmarkTiming();
+       usleep(10000);
+    }
+}
+BENCHMARK(BM_log_light_overhead);
+
+static void BM_log_light_overhead_null(int iters) {
+    set_log_null();
+    BM_log_light_overhead(iters);
+    set_log_default();
+}
+BENCHMARK(BM_log_light_overhead_null);
 
 static void caught_latency(int /*signum*/)
 {
@@ -695,7 +793,7 @@
 
 // Keep maps around for multiple iterations
 static std::unordered_set<uint32_t> set;
-static const EventTagMap* map;
+static EventTagMap* map;
 
 static bool prechargeEventMap() {
     if (map) return true;
@@ -785,3 +883,142 @@
     StopBenchmarkTiming();
 }
 BENCHMARK(BM_lookupEventFormat);
+
+/*
+ *	Measure the time it takes for android_lookupEventTagNum plus above
+ */
+static void BM_lookupEventTagNum(int iters) {
+
+    prechargeEventMap();
+
+    std::unordered_set<uint32_t>::const_iterator it = set.begin();
+
+    for (int i = 0; i < iters; ++i) {
+        size_t len;
+        const char* name = android_lookupEventTag_len(map, &len, (*it));
+        std::string Name(name, len);
+        const char* format = android_lookupEventFormat_len(map, &len, (*it));
+        std::string Format(format, len);
+        StartBenchmarkTiming();
+        android_lookupEventTagNum(map, Name.c_str(), Format.c_str(),
+                                  ANDROID_LOG_UNKNOWN);
+        StopBenchmarkTiming();
+        ++it;
+        if (it == set.end()) it = set.begin();
+    }
+
+}
+BENCHMARK(BM_lookupEventTagNum);
+
+// Must be functionally identical to liblog internal __send_log_msg.
+static void send_to_control(char *buf, size_t len)
+{
+    int sock = socket_local_client("logd",
+                                   ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                   SOCK_STREAM);
+    if (sock < 0) return;
+    size_t writeLen = strlen(buf) + 1;
+
+    ssize_t ret = TEMP_FAILURE_RETRY(write(sock, buf, writeLen));
+    if (ret <= 0) {
+        close(sock);
+        return;
+    }
+    while ((ret = read(sock, buf, len)) > 0) {
+        if (((size_t)ret == len) || (len < PAGE_SIZE)) {
+            break;
+        }
+        len -= ret;
+        buf += ret;
+
+        struct pollfd p = {
+            .fd = sock,
+            .events = POLLIN,
+            .revents = 0
+        };
+
+        ret = poll(&p, 1, 20);
+        if ((ret <= 0) || !(p.revents & POLLIN)) {
+            break;
+        }
+    }
+    close(sock);
+}
+
+static void BM_lookupEventTagNum_logd_new(int iters) {
+    fprintf(stderr, "WARNING: "
+            "This test can cause logd to grow in size and hit DOS limiter\n");
+    // Make copies
+    static const char empty_event_log_tags[] = "# content owned by logd\n";
+    static const char dev_event_log_tags_path[] = "/dev/event-log-tags";
+    std::string dev_event_log_tags;
+    if (android::base::ReadFileToString(dev_event_log_tags_path,
+                                        &dev_event_log_tags) &&
+            (dev_event_log_tags.length() == 0)) {
+        dev_event_log_tags = empty_event_log_tags;
+    }
+    static const char data_event_log_tags_path[] = "/data/misc/logd/event-log-tags";
+    std::string data_event_log_tags;
+    if (android::base::ReadFileToString(data_event_log_tags_path,
+                                        &data_event_log_tags) &&
+            (data_event_log_tags.length() == 0)) {
+        data_event_log_tags = empty_event_log_tags;
+    }
+
+    for (int i = 0; i < iters; ++i) {
+        char buffer[256];
+        memset(buffer, 0, sizeof(buffer));
+        log_time now(CLOCK_MONOTONIC);
+        char name[64];
+        snprintf(name, sizeof(name), "a%" PRIu64, now.nsec());
+        snprintf(buffer, sizeof(buffer),
+                 "getEventTag name=%s format=\"(new|1)\"", name);
+        StartBenchmarkTiming();
+        send_to_control(buffer, sizeof(buffer));
+        StopBenchmarkTiming();
+    }
+
+    // Restore copies (logd still know about them, until crash or reboot)
+    if (dev_event_log_tags.length() &&
+            !android::base::WriteStringToFile(dev_event_log_tags,
+                                              dev_event_log_tags_path)) {
+        fprintf(stderr, "WARNING: "
+                "failed to restore %s\n", dev_event_log_tags_path);
+    }
+    if (data_event_log_tags.length() &&
+            !android::base::WriteStringToFile(data_event_log_tags,
+                                              data_event_log_tags_path)) {
+        fprintf(stderr, "WARNING: "
+                "failed to restore %s\n", data_event_log_tags_path);
+    }
+    fprintf(stderr, "WARNING: "
+            "Restarting logd to make it forget what we just did\n");
+    system("stop logd ; start logd");
+}
+BENCHMARK(BM_lookupEventTagNum_logd_new);
+
+static void BM_lookupEventTagNum_logd_existing(int iters) {
+    prechargeEventMap();
+
+    std::unordered_set<uint32_t>::const_iterator it = set.begin();
+
+    for (int i = 0; i < iters; ++i) {
+        size_t len;
+        const char* name = android_lookupEventTag_len(map, &len, (*it));
+        std::string Name(name, len);
+        const char* format = android_lookupEventFormat_len(map, &len, (*it));
+        std::string Format(format, len);
+
+        char buffer[256];
+        snprintf(buffer, sizeof(buffer),
+                 "getEventTag name=%s format=\"%s\"",
+                 Name.c_str(), Format.c_str());
+
+        StartBenchmarkTiming();
+        send_to_control(buffer, sizeof(buffer));
+        StopBenchmarkTiming();
+        ++it;
+        if (it == set.end()) it = set.begin();
+    }
+}
+BENCHMARK(BM_lookupEventTagNum_logd_existing);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index d80f8b2..bc0ea4c 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -19,6 +19,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <pthread.h>
 #include <semaphore.h>
 #include <signal.h>
 #include <stdio.h>
@@ -36,9 +37,32 @@
 #include <gtest/gtest.h>
 #include <log/logprint.h>
 #include <log/log_event_list.h>
+#include <log/log_frontend.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
+#ifndef TEST_PREFIX
+#ifdef __ANDROID__ // make sure we always run code if compiled for android
+#define TEST_PREFIX
+#endif
+#endif
+
+#if (!defined(USING_LOGGER_DEFAULT) || !defined(USING_LOGGER_LOCAL))
+#ifdef liblog // a binary clue that we are overriding the test names
+// Does not support log reading blocking feature yet
+// Does not support LOG_ID_SECURITY (unless we set LOGGER_LOCAL | LOGGER_LOGD)
+// Assume some common aspects are tested by USING_LOGGER_DEFAULT:
+// Does not need to _retest_ pmsg functionality
+// Does not need to _retest_ property handling as it is a higher function
+// Does not need to _retest_ event mapping functionality
+// Does not need to _retest_ ratelimit
+// Does not need to _retest_ logprint
+#define USING_LOGGER_LOCAL
+#else
+#define USING_LOGGER_DEFAULT
+#endif
+#endif
+
 // 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
@@ -54,46 +78,10 @@
           || (_rc == -EAGAIN));    \
     _rc; })
 
-TEST(liblog, __android_log_buf_print) {
-#ifdef __ANDROID__
-    EXPECT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO,
-                                         "TEST__android_log_buf_print",
-                                         "radio"));
-    usleep(1000);
-    EXPECT_LT(0, __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
-                                         "TEST__android_log_buf_print",
-                                         "system"));
-    usleep(1000);
-    EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
-                                         "TEST__android_log_buf_print",
-                                         "main"));
-    usleep(1000);
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-
-TEST(liblog, __android_log_buf_write) {
-#ifdef __ANDROID__
-    EXPECT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO,
-                                         "TEST__android_log_buf_write",
-                                         "radio"));
-    usleep(1000);
-    EXPECT_LT(0, __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
-                                         "TEST__android_log_buf_write",
-                                         "system"));
-    usleep(1000);
-    EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
-                                         "TEST__android_log_buf_write",
-                                         "main"));
-    usleep(1000);
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-
 TEST(liblog, __android_log_btwrite) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+    TEST_PREFIX
+#endif
     int intBuf = 0xDEADBEEF;
     EXPECT_LT(0, __android_log_btwrite(0,
                                       EVENT_TYPE_INT,
@@ -108,50 +96,10 @@
                                       EVENT_TYPE_STRING,
                                       Buf, sizeof(Buf) - 1));
     usleep(1000);
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
 }
 
-#ifdef __ANDROID__
-static void* ConcurrentPrintFn(void *arg) {
-    int ret = __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
-                                  "TEST__android_log_print", "Concurrent %" PRIuPTR,
-                                  reinterpret_cast<uintptr_t>(arg));
-    return reinterpret_cast<void*>(ret);
-}
-#endif
-
-#define NUM_CONCURRENT 64
-#define _concurrent_name(a,n) a##__concurrent##n
-#define concurrent_name(a,n) _concurrent_name(a,n)
-
-TEST(liblog, concurrent_name(__android_log_buf_print, NUM_CONCURRENT)) {
-#ifdef __ANDROID__
-    pthread_t t[NUM_CONCURRENT];
-    int i;
-    for (i=0; i < NUM_CONCURRENT; i++) {
-        ASSERT_EQ(0, pthread_create(&t[i], NULL,
-                                    ConcurrentPrintFn,
-                                    reinterpret_cast<void *>(i)));
-    }
-    int ret = 0;
-    for (i=0; i < NUM_CONCURRENT; i++) {
-        void* result;
-        ASSERT_EQ(0, pthread_join(t[i], &result));
-        int this_result = reinterpret_cast<uintptr_t>(result);
-        if ((0 == ret) && (0 != this_result)) {
-            ret = this_result;
-        }
-    }
-    ASSERT_LT(0, ret);
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-
-#ifdef __ANDROID__
-std::string popenToString(std::string command) {
+#if (defined(__ANDROID__) && !defined(USING_LOGGER_LOCAL))
+static std::string popenToString(std::string command) {
     std::string ret;
 
     FILE* fp = popen(command.c_str(), "r");
@@ -214,10 +162,12 @@
     }
     return false;
 }
+
+static bool tested__android_log_close;
 #endif
 
 TEST(liblog, __android_log_btwrite__android_logger_list_read) {
-#ifdef __ANDROID__
+#if (defined(__ANDROID__) || defined(USING_LOGGER_LOCAL))
     struct logger_list *logger_list;
 
     pid_t pid = getpid();
@@ -225,25 +175,40 @@
     ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
         LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
 
-    // Check that we can close and reopen the logger
     log_time ts(CLOCK_MONOTONIC);
     ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
-    bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
-    bool logdwActiveAfter__android_log_btwrite = isLogdwActive();
-    EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
-    EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
+#ifndef USING_LOGGER_LOCAL
+    // Check that we can close and reopen the logger
+    bool pmsgActiveAfter__android_log_btwrite;
+    bool logdwActiveAfter__android_log_btwrite;
+    if (getuid() == AID_ROOT) {
+        tested__android_log_close = true;
+        pmsgActiveAfter__android_log_btwrite = isPmsgActive();
+        logdwActiveAfter__android_log_btwrite = isLogdwActive();
+        EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+        EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
+    } else if (!tested__android_log_close) {
+        fprintf(stderr, "WARNING: can not test __android_log_close()\n");
+    }
     __android_log_close();
-    bool pmsgActiveAfter__android_log_close = isPmsgActive();
-    bool logdwActiveAfter__android_log_close = isLogdwActive();
-    EXPECT_FALSE(pmsgActiveAfter__android_log_close);
-    EXPECT_FALSE(logdwActiveAfter__android_log_close);
+    if (getuid() == AID_ROOT) {
+        bool pmsgActiveAfter__android_log_close = isPmsgActive();
+        bool logdwActiveAfter__android_log_close = isLogdwActive();
+        EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+        EXPECT_FALSE(logdwActiveAfter__android_log_close);
+    }
+#endif
 
     log_time ts1(CLOCK_MONOTONIC);
     ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
-    pmsgActiveAfter__android_log_btwrite = isPmsgActive();
-    logdwActiveAfter__android_log_btwrite = isLogdwActive();
-    EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
-    EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
+#ifndef USING_LOGGER_LOCAL
+    if (getuid() == AID_ROOT) {
+        pmsgActiveAfter__android_log_btwrite = isPmsgActive();
+        logdwActiveAfter__android_log_btwrite = isLogdwActive();
+        EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+        EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
+    }
+#endif
     usleep(1000000);
 
     int count = 0;
@@ -257,18 +222,19 @@
 
         ASSERT_EQ(log_msg.entry.pid, pid);
 
-        if ((log_msg.entry.len != (4 + 1 + 8))
+        if ((log_msg.entry.len != sizeof(android_log_event_long_t))
          || (log_msg.id() != LOG_ID_EVENTS)) {
             continue;
         }
 
-        char *eventData = log_msg.msg();
+        android_log_event_long_t* eventData;
+        eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
 
-        if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
+        if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
             continue;
         }
 
-        log_time tx(eventData + 4 + 1);
+        log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
         if (ts == tx) {
             ++count;
         } else if (ts1 == tx) {
@@ -285,15 +251,92 @@
 #endif
 }
 
-#ifdef __ANDROID__
-static inline int32_t get4LE(const char* src)
-{
+// This test makes little sense standalone, and requires the tests ahead
+// and behind us, to make us whole.  We could incorporate a prefix and
+// suffix test to make this standalone, but opted to not complicate this.
+TEST(liblog, android_set_log_frontend) {
+#if (defined(__ANDROID__) || defined(USING_LOGGER_LOCAL))
+#ifdef TEST_PREFIX
+    TEST_PREFIX
+#endif
+
+    int logger = android_get_log_frontend();
+    EXPECT_NE(LOGGER_NULL, logger);
+
+    EXPECT_EQ(LOGGER_NULL, android_set_log_frontend(LOGGER_NULL));
+    EXPECT_EQ(LOGGER_NULL, android_get_log_frontend());
+
+    pid_t pid = getpid();
+
+    struct logger_list *logger_list;
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    log_time ts(CLOCK_MONOTONIC);
+    ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+
+    usleep(1000000);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        ASSERT_EQ(log_msg.entry.pid, pid);
+
+        if ((log_msg.entry.len != sizeof(android_log_event_long_t))
+         || (log_msg.id() != LOG_ID_EVENTS)) {
+            continue;
+        }
+
+        android_log_event_long_t* eventData;
+        eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
+
+        if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
+            continue;
+        }
+
+        log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
+        if (ts == tx) {
+            ++count;
+        }
+    }
+
+    android_logger_list_close(logger_list);
+
+    EXPECT_EQ(logger, android_set_log_frontend(logger));
+    EXPECT_EQ(logger, android_get_log_frontend());
+
+    // False negative if liblog.__android_log_btwrite__android_logger_list_read
+    // fails above, so we will likely succeed. But we will have so many
+    // failures elsewhere that it is probably not worthwhile for us to
+    // highlight yet another disappointment.
+    EXPECT_EQ(0, count);
+    // We also expect failures in the following tests if the set does not
+    // react in an appropriate manner internally, yet passes, so we depend
+    // on this test being in the middle of a series of tests performed in
+    // the same process.
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+#ifdef TEST_PREFIX
+static inline uint32_t get4LE(const uint8_t* src) {
     return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
 }
+
+static inline uint32_t get4LE(const char* src) {
+    return get4LE(reinterpret_cast<const uint8_t*>(src));
+}
 #endif
 
 static void bswrite_test(const char *message) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+    TEST_PREFIX
     struct logger_list *logger_list;
 
     pid_t pid = getpid();
@@ -301,7 +344,11 @@
     ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
         LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
 
+#ifdef __ANDROID__
     log_time ts(android_log_clockid());
+#else
+    log_time ts(CLOCK_REALTIME);
+#endif
 
     ASSERT_LT(0, __android_log_bswrite(0, message));
     size_t num_lines = 1, size = 0, length = 0, total = 0;
@@ -339,18 +386,20 @@
 
         if ((log_msg.entry.sec < (ts.tv_sec - 1))
          || ((ts.tv_sec + 1) < log_msg.entry.sec)
-         || ((size_t)log_msg.entry.len != (4 + 1 + 4 + length))
+         || ((size_t)log_msg.entry.len != (sizeof(android_log_event_string_t) +
+                                           length))
          || (log_msg.id() != LOG_ID_EVENTS)) {
             continue;
         }
 
-        char *eventData = log_msg.msg();
+        android_log_event_string_t* eventData;
+        eventData = reinterpret_cast<android_log_event_string_t*>(log_msg.msg());
 
-        if (!eventData || (eventData[4] != EVENT_TYPE_STRING)) {
+        if (!eventData || (eventData->type != EVENT_TYPE_STRING)) {
             continue;
         }
 
-        size_t len = get4LE(eventData + 4 + 1);
+        size_t len = get4LE(reinterpret_cast<char*>(&eventData->length));
         if (len == total) {
             ++count;
 
@@ -365,8 +414,11 @@
                 &log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
             EXPECT_EQ((length == total) ? 0 : -1, processBinaryLogBuffer);
             if (processBinaryLogBuffer == 0) {
+                size_t line_overhead = 20;
+                if (pid > 99999) ++line_overhead;
+                if (pid > 999999) ++line_overhead;
                 fflush(stderr);
-                EXPECT_EQ((int)((20 * num_lines) + size),
+                EXPECT_EQ((int)((line_overhead * num_lines) + size),
                     android_log_printLogLine(logformat, fileno(stderr), &entry));
             }
             android_log_format_free(logformat);
@@ -403,7 +455,8 @@
 }
 
 static void buf_write_test(const char *message) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+    TEST_PREFIX
     struct logger_list *logger_list;
 
     pid_t pid = getpid();
@@ -412,7 +465,11 @@
         LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
 
     static const char tag[] = "TEST__android_log_buf_write";
+#ifdef __ANDROID__
     log_time ts(android_log_clockid());
+#else
+    log_time ts(CLOCK_REALTIME);
+#endif
 
     EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
                                          tag, message));
@@ -460,8 +517,11 @@
                                                             &entry);
         EXPECT_EQ(0, processLogBuffer);
         if (processLogBuffer == 0) {
+            size_t line_overhead = 11;
+            if (pid > 99999) ++line_overhead;
+            if (pid > 999999) ++line_overhead;
             fflush(stderr);
-            EXPECT_EQ((int)(((11 + sizeof(tag)) * num_lines) + size),
+            EXPECT_EQ((int)(((line_overhead + sizeof(tag)) * num_lines) + size),
                 android_log_printLogLine(logformat, fileno(stderr), &entry));
         }
         android_log_format_free(logformat);
@@ -488,188 +548,8 @@
     buf_write_test("\n Hello World \n");
 }
 
-TEST(liblog, __security) {
-#ifdef __ANDROID__
-    static const char persist_key[] = "persist.logd.security";
-    static const char readonly_key[] = "ro.device_owner";
-    static const char nothing_val[] = "_NOTHING_TO_SEE_HERE_";
-    char persist[PROP_VALUE_MAX];
-    char readonly[PROP_VALUE_MAX];
-
-    property_get(persist_key, persist, "");
-    property_get(readonly_key, readonly, nothing_val);
-
-    if (!strcmp(readonly, nothing_val)) {
-        EXPECT_FALSE(__android_log_security());
-        fprintf(stderr, "Warning, setting ro.device_owner to a domain\n");
-        property_set(readonly_key, "com.google.android.SecOps.DeviceOwner");
-    } else if (!strcasecmp(readonly, "false") || !readonly[0]) {
-        EXPECT_FALSE(__android_log_security());
-        return;
-    }
-
-    if (!strcasecmp(persist, "true")) {
-        EXPECT_TRUE(__android_log_security());
-    } else {
-        EXPECT_FALSE(__android_log_security());
-    }
-    property_set(persist_key, "TRUE");
-    EXPECT_TRUE(__android_log_security());
-    property_set(persist_key, "FALSE");
-    EXPECT_FALSE(__android_log_security());
-    property_set(persist_key, "true");
-    EXPECT_TRUE(__android_log_security());
-    property_set(persist_key, "false");
-    EXPECT_FALSE(__android_log_security());
-    property_set(persist_key, "");
-    EXPECT_FALSE(__android_log_security());
-    property_set(persist_key, persist);
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-
-TEST(liblog, __security_buffer) {
-#ifdef __ANDROID__
-    struct logger_list *logger_list;
-    android_event_long_t buffer;
-
-    static const char persist_key[] = "persist.logd.security";
-    char persist[PROP_VALUE_MAX];
-    bool set_persist = false;
-    bool allow_security = false;
-
-    if (__android_log_security()) {
-        allow_security = true;
-    } else {
-        property_get(persist_key, persist, "");
-        if (strcasecmp(persist, "true")) {
-            property_set(persist_key, "TRUE");
-            if (__android_log_security()) {
-                allow_security = true;
-                set_persist = true;
-            } else {
-                property_set(persist_key, persist);
-            }
-        }
-    }
-
-    if (!allow_security) {
-        fprintf(stderr, "WARNING: "
-                "security buffer disabled, bypassing end-to-end test\n");
-
-        log_time ts(CLOCK_MONOTONIC);
-
-        buffer.type = EVENT_TYPE_LONG;
-        buffer.data = *(static_cast<uint64_t *>((void *)&ts));
-
-        // expect failure!
-        ASSERT_GE(0, __android_log_security_bwrite(0, &buffer, sizeof(buffer)));
-
-        return;
-    }
-
-    /* Matches clientHasLogCredentials() in logd */
-    uid_t uid = getuid();
-    gid_t gid = getgid();
-    bool clientHasLogCredentials = true;
-    if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)
-     && (gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
-        uid_t euid = geteuid();
-        if ((euid != AID_SYSTEM) && (euid != AID_ROOT) && (euid != AID_LOG)) {
-            gid_t egid = getegid();
-            if ((egid != AID_SYSTEM) && (egid != AID_ROOT) && (egid != AID_LOG)) {
-                int num_groups = getgroups(0, NULL);
-                if (num_groups > 0) {
-                    gid_t groups[num_groups];
-                    num_groups = getgroups(num_groups, groups);
-                    while (num_groups > 0) {
-                        if (groups[num_groups - 1] == AID_LOG) {
-                            break;
-                        }
-                        --num_groups;
-                    }
-                }
-                if (num_groups <= 0) {
-                    clientHasLogCredentials = false;
-                }
-            }
-        }
-    }
-    if (!clientHasLogCredentials) {
-        fprintf(stderr, "WARNING: "
-                "not in system context, bypassing end-to-end test\n");
-
-        log_time ts(CLOCK_MONOTONIC);
-
-        buffer.type = EVENT_TYPE_LONG;
-        buffer.data = *(static_cast<uint64_t *>((void *)&ts));
-
-        // expect failure!
-        ASSERT_GE(0, __android_log_security_bwrite(0, &buffer, sizeof(buffer)));
-
-        return;
-    }
-
-    pid_t pid = getpid();
-
-    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
-        LOG_ID_SECURITY, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
-        1000, pid)));
-
-    log_time ts(CLOCK_MONOTONIC);
-
-    buffer.type = EVENT_TYPE_LONG;
-    buffer.data = *(static_cast<uint64_t *>((void *)&ts));
-
-    ASSERT_LT(0, __android_log_security_bwrite(0, &buffer, sizeof(buffer)));
-    usleep(1000000);
-
-    int count = 0;
-
-    for (;;) {
-        log_msg log_msg;
-        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
-            break;
-        }
-
-        ASSERT_EQ(log_msg.entry.pid, pid);
-
-        if ((log_msg.entry.len != (4 + 1 + 8))
-         || (log_msg.id() != LOG_ID_SECURITY)) {
-            continue;
-        }
-
-        char *eventData = log_msg.msg();
-
-        if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
-            continue;
-        }
-
-        log_time tx(eventData + 4 + 1);
-        if (ts == tx) {
-            ++count;
-        }
-    }
-
-    if (set_persist) {
-        property_set(persist_key, persist);
-    }
-
-    android_logger_list_close(logger_list);
-
-    bool clientHasSecurityCredentials = (uid == AID_SYSTEM) || (gid == AID_SYSTEM);
-    if (!clientHasSecurityCredentials) {
-        fprintf(stderr, "WARNING: "
-                "not system, content submitted but can not check end-to-end\n");
-    }
-    EXPECT_EQ(clientHasSecurityCredentials ? 1 : 0, count);
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-
-#ifdef __ANDROID__
+#ifndef USING_LOGGER_LOCAL // requires blocking reader functionality
+#ifdef TEST_PREFIX
 static unsigned signaled;
 static log_time signal_time;
 
@@ -731,7 +611,8 @@
 #endif
 
 TEST(liblog, android_logger_list_read__cpu_signal) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+    TEST_PREFIX
     struct logger_list *logger_list;
     unsigned long long v = 0xDEADBEEFA55A0000ULL;
 
@@ -771,25 +652,27 @@
 
         ASSERT_EQ(log_msg.entry.pid, pid);
 
-        if ((log_msg.entry.len != (4 + 1 + 8))
+        if ((log_msg.entry.len != sizeof(android_log_event_long_t))
          || (log_msg.id() != LOG_ID_EVENTS)) {
             continue;
         }
 
-        char *eventData = log_msg.msg();
+        android_log_event_long_t* eventData;
+        eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
 
-        if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
+        if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
             continue;
         }
 
-        unsigned long long l = eventData[4 + 1 + 0] & 0xFF;
-        l |= (unsigned long long) (eventData[4 + 1 + 1] & 0xFF) << 8;
-        l |= (unsigned long long) (eventData[4 + 1 + 2] & 0xFF) << 16;
-        l |= (unsigned long long) (eventData[4 + 1 + 3] & 0xFF) << 24;
-        l |= (unsigned long long) (eventData[4 + 1 + 4] & 0xFF) << 32;
-        l |= (unsigned long long) (eventData[4 + 1 + 5] & 0xFF) << 40;
-        l |= (unsigned long long) (eventData[4 + 1 + 6] & 0xFF) << 48;
-        l |= (unsigned long long) (eventData[4 + 1 + 7] & 0xFF) << 56;
+        char* cp = reinterpret_cast<char*>(&eventData->payload.data);
+        unsigned long long l = cp[0] & 0xFF;
+        l |= (unsigned long long) (cp[1] & 0xFF) << 8;
+        l |= (unsigned long long) (cp[2] & 0xFF) << 16;
+        l |= (unsigned long long) (cp[3] & 0xFF) << 24;
+        l |= (unsigned long long) (cp[4] & 0xFF) << 32;
+        l |= (unsigned long long) (cp[5] & 0xFF) << 40;
+        l |= (unsigned long long) (cp[6] & 0xFF) << 48;
+        l |= (unsigned long long) (cp[7] & 0xFF) << 56;
 
         if (l == v) {
             ++signals;
@@ -821,7 +704,7 @@
 #endif
 }
 
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
 /*
  *  Strictly, we are not allowed to log messages in a signal context, the
  * correct way to handle this is to ensure the messages are constructed in
@@ -855,7 +738,7 @@
     return NULL;
 }
 
-int start_thread()
+static int start_thread()
 {
     sem_init(&thread_trigger, 0, 0);
 
@@ -887,7 +770,8 @@
 #endif
 
 TEST(liblog, android_logger_list_read__cpu_thread) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+    TEST_PREFIX
     struct logger_list *logger_list;
     unsigned long long v = 0xDEADBEAFA55A0000ULL;
 
@@ -928,25 +812,27 @@
 
         ASSERT_EQ(log_msg.entry.pid, pid);
 
-        if ((log_msg.entry.len != (4 + 1 + 8))
+        if ((log_msg.entry.len != sizeof(android_log_event_long_t))
          || (log_msg.id() != LOG_ID_EVENTS)) {
             continue;
         }
 
-        char *eventData = log_msg.msg();
+        android_log_event_long_t* eventData;
+        eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
 
-        if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
+        if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
             continue;
         }
 
-        unsigned long long l = eventData[4 + 1 + 0] & 0xFF;
-        l |= (unsigned long long) (eventData[4 + 1 + 1] & 0xFF) << 8;
-        l |= (unsigned long long) (eventData[4 + 1 + 2] & 0xFF) << 16;
-        l |= (unsigned long long) (eventData[4 + 1 + 3] & 0xFF) << 24;
-        l |= (unsigned long long) (eventData[4 + 1 + 4] & 0xFF) << 32;
-        l |= (unsigned long long) (eventData[4 + 1 + 5] & 0xFF) << 40;
-        l |= (unsigned long long) (eventData[4 + 1 + 6] & 0xFF) << 48;
-        l |= (unsigned long long) (eventData[4 + 1 + 7] & 0xFF) << 56;
+        char* cp = reinterpret_cast<char*>(&eventData->payload.data);
+        unsigned long long l = cp[0] & 0xFF;
+        l |= (unsigned long long) (cp[1] & 0xFF) << 8;
+        l |= (unsigned long long) (cp[2] & 0xFF) << 16;
+        l |= (unsigned long long) (cp[3] & 0xFF) << 24;
+        l |= (unsigned long long) (cp[4] & 0xFF) << 32;
+        l |= (unsigned long long) (cp[5] & 0xFF) << 40;
+        l |= (unsigned long long) (cp[6] & 0xFF) << 48;
+        l |= (unsigned long long) (cp[7] & 0xFF) << 56;
 
         if (l == v) {
             ++signals;
@@ -977,8 +863,9 @@
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
+#endif // !USING_LOGGER_LOCAL
 
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
 static const char max_payload_tag[] = "TEST_max_payload_and_longish_tag_XXXX";
 #define SIZEOF_MAX_PAYLOAD_BUF (LOGGER_ENTRY_MAX_PAYLOAD - \
                                 sizeof(max_payload_tag) - 1)
@@ -1115,7 +1002,8 @@
 takes his leave.";
 
 TEST(liblog, max_payload) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+    TEST_PREFIX
     pid_t pid = getpid();
     char tag[sizeof(max_payload_tag)];
     memcpy(tag, max_payload_tag, sizeof(tag));
@@ -1179,7 +1067,8 @@
 }
 
 TEST(liblog, __android_log_buf_print__maxtag) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+    TEST_PREFIX
     struct logger_list *logger_list;
 
     pid_t pid = getpid();
@@ -1187,7 +1076,11 @@
     ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
         LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
 
+#ifdef __ANDROID__
     log_time ts(android_log_clockid());
+#else
+    log_time ts(CLOCK_REALTIME);
+#endif
 
     EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
                                          max_payload_buf, max_payload_buf));
@@ -1239,7 +1132,8 @@
 }
 
 TEST(liblog, too_big_payload) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+    TEST_PREFIX
     pid_t pid = getpid();
     static const char big_payload_tag[] = "TEST_big_payload_XXXX";
     char tag[sizeof(big_payload_tag)];
@@ -1293,6 +1187,12 @@
     EXPECT_LE(LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag),
               static_cast<size_t>(max_len));
 
+    // SLOP: Allow the underlying interface to optionally place a
+    // terminating nul at the LOGGER_ENTRY_MAX_PAYLOAD's last byte
+    // or not.
+    if (ret == (max_len + static_cast<ssize_t>(sizeof(big_payload_tag)) - 1)) {
+        --max_len;
+    }
     EXPECT_EQ(ret, max_len + static_cast<ssize_t>(sizeof(big_payload_tag)));
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -1300,17 +1200,29 @@
 }
 
 TEST(liblog, dual_reader) {
-#ifdef __ANDROID__
-    struct logger_list *logger_list1;
+#ifdef TEST_PREFIX
+    TEST_PREFIX
 
-    // >25 messages due to liblog.__android_log_buf_print__concurrentXX above.
+    static const int num = 25;
+
+    for (int i = 25; i > 0; --i) {
+        static const char fmt[] = "dual_reader %02d";
+        char buffer[sizeof(fmt) + 8];
+        snprintf(buffer, sizeof(buffer), fmt, i);
+        LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_MAIN,
+                                                  ANDROID_LOG_INFO,
+                                                  "liblog", buffer));
+    }
+    usleep(1000000);
+
+    struct logger_list *logger_list1;
     ASSERT_TRUE(NULL != (logger_list1 = android_logger_list_open(
-        LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 25, 0)));
+        LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, num, 0)));
 
     struct logger_list *logger_list2;
 
     if (NULL == (logger_list2 = android_logger_list_open(
-            LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 15, 0))) {
+            LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, num - 10, 0))) {
         android_logger_list_close(logger_list1);
         ASSERT_TRUE(NULL != logger_list2);
     }
@@ -1343,60 +1255,20 @@
     android_logger_list_close(logger_list1);
     android_logger_list_close(logger_list2);
 
-    EXPECT_EQ(25, count1);
-    EXPECT_EQ(15, count2);
+    EXPECT_EQ(num, count1);
+    EXPECT_EQ(num - 10, count2);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
-TEST(liblog, android_logger_get_) {
-#ifdef __ANDROID__
-    struct logger_list * logger_list = android_logger_list_alloc(ANDROID_LOG_WRONLY, 0, 0);
-
-    for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
-        log_id_t id = static_cast<log_id_t>(i);
-        const char *name = android_log_id_to_name(id);
-        if (id != android_name_to_log_id(name)) {
-            continue;
-        }
-        fprintf(stderr, "log buffer %s\r", name);
-        struct logger * logger;
-        EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
-        EXPECT_EQ(id, android_logger_get_id(logger));
-        ssize_t get_log_size = android_logger_get_log_size(logger);
-        /* security buffer is allowed to be denied */
-        if (strcmp("security", name)) {
-            EXPECT_LT(0, get_log_size);
-            /* crash buffer is allowed to be empty, that is actually healthy! */
-            EXPECT_LE((strcmp("crash", name)) != 0,
-                      android_logger_get_log_readable_size(logger));
-        } else {
-            EXPECT_NE(0, get_log_size);
-            if (get_log_size < 0) {
-                EXPECT_GT(0, android_logger_get_log_readable_size(logger));
-            } else {
-                EXPECT_LE(0, android_logger_get_log_readable_size(logger));
-            }
-        }
-        EXPECT_LT(0, android_logger_get_log_version(logger));
-    }
-
-    android_logger_list_close(logger_list);
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-
-#ifdef __ANDROID__
+#ifndef USING_LOGGER_LOCAL // Do not retest logprint
 static bool checkPriForTag(AndroidLogFormat *p_format, const char *tag, android_LogPriority pri) {
     return android_log_shouldPrintLine(p_format, tag, pri)
         && !android_log_shouldPrintLine(p_format, tag, (android_LogPriority)(pri - 1));
 }
-#endif
 
 TEST(liblog, filterRule) {
-#ifdef __ANDROID__
     static const char tag[] = "random";
 
     AndroidLogFormat *p_format = android_log_format_new();
@@ -1458,11 +1330,10 @@
 #endif
 
     android_log_format_free(p_format);
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
 }
+#endif // !USING_LOGGER_LOCAL
 
+#ifndef USING_LOGGER_LOCAL // Do not retest property handling
 TEST(liblog, is_loggable) {
 #ifdef __ANDROID__
     static const char tag[] = "is_loggable";
@@ -1761,24 +1632,258 @@
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
+#endif // !USING_LOGGER_LOCAL
 
-TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__typical) {
+// Following tests the specific issues surrounding error handling wrt logd.
+// Kills logd and toss all collected data, equivalent to logcat -b all -c,
+// except we also return errors to the logging callers.
+#ifndef USING_LOGGER_LOCAL
+#ifdef TEST_PREFIX
+// helper to liblog.enoent to count end-to-end matching logging messages.
+static int count_matching_ts(log_time ts) {
+    usleep(1000000);
+
+    pid_t pid = getpid();
+
+    struct logger_list* logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid);
+
+    int count = 0;
+    if (logger_list == NULL) return count;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
+
+        if (log_msg.entry.len != sizeof(android_log_event_long_t)) continue;
+        if (log_msg.id() != LOG_ID_EVENTS) continue;
+
+        android_log_event_long_t* eventData;
+        eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
+        if (!eventData) continue;
+        if (eventData->payload.type != EVENT_TYPE_LONG) continue;
+
+        log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
+        if (ts != tx) continue;
+
+        // found event message with matching timestamp signature in payload
+        ++count;
+    }
+    android_logger_list_close(logger_list);
+
+    return count;
+}
+
+// meant to be handed to ASSERT_TRUE / EXPECT_TRUE only to expand the message
+static testing::AssertionResult IsOk(bool ok, std::string &message) {
+    return ok ?
+        testing::AssertionSuccess() :
+        (testing::AssertionFailure() << message);
+}
+#endif // TEST_PREFIX
+
+TEST(liblog, enoent) {
+#ifdef TEST_PREFIX
+    TEST_PREFIX
+    log_time ts(CLOCK_MONOTONIC);
+    EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+    EXPECT_EQ(1, count_matching_ts(ts));
+
+    // This call will fail if we are setuid(AID_SYSTEM), beware of any
+    // test prior to this one playing with setuid and causing interference.
+    // We need to run before these tests so that they do not interfere with
+    // this test.
+    //
+    // Stopping the logger can affect some other test's expectations as they
+    // count on the log buffers filled with existing content, and this
+    // effectively does a logcat -c emptying it.  So we want this test to be
+    // as near as possible to the bottom of the file.  For example
+    // liblog.android_logger_get_ is one of those tests that has no recourse
+    // and that would be adversely affected by emptying the log if it was run
+    // right after this test.
+    system("stop logd");
+    usleep(1000000);
+
+    // A clean stop like we are testing returns -ENOENT, but in the _real_
+    // world we could get -ENOTCONN or -ECONNREFUSED depending on timing.
+    // Alas we can not test these other return values; accept that they
+    // are treated equally within the open-retry logic in liblog.
+    ts = log_time(CLOCK_MONOTONIC);
+    int ret = __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts));
+    std::string content = android::base::StringPrintf(
+        "__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) = %d %s\n",
+        ret, strerror(-ret));
+    EXPECT_TRUE(IsOk((ret == -ENOENT) ||
+                     (ret == -ENOTCONN) ||
+                     (ret == -ECONNREFUSED), content));
+    ret = __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts));
+    content = android::base::StringPrintf(
+        "__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) = %d %s\n",
+        ret, strerror(-ret));
+    EXPECT_TRUE(IsOk((ret == -ENOENT) ||
+                     (ret == -ENOTCONN) ||
+                     (ret == -ECONNREFUSED), content));
+    EXPECT_EQ(0, count_matching_ts(ts));
+
+    system("start logd");
+    usleep(1000000);
+
+    EXPECT_EQ(0, count_matching_ts(ts));
+
+    ts = log_time(CLOCK_MONOTONIC);
+    EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+    EXPECT_EQ(1, count_matching_ts(ts));
+
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+#endif // !USING_LOCAL_LOGGER
+
+// Below this point we run risks of setuid(AID_SYSTEM) which may affect others.
+
+// Do not retest properties, and cannot log into LOG_ID_SECURITY
+#ifndef USING_LOGGER_LOCAL
+TEST(liblog, __security) {
 #ifdef __ANDROID__
-    const int TAG = 123456781;
-    const char SUBTAG[] = "test-subtag";
-    const int UID = -1;
-    const int DATA_LEN = 200;
+    static const char persist_key[] = "persist.logd.security";
+    static const char readonly_key[] = "ro.device_owner";
+    // A silly default value that can never be in readonly_key so
+    // that it can be determined the property is not set.
+    static const char nothing_val[] = "_NOTHING_TO_SEE_HERE_";
+    char persist[PROP_VALUE_MAX];
+    char readonly[PROP_VALUE_MAX];
+
+    property_get(persist_key, persist, "");
+    property_get(readonly_key, readonly, nothing_val);
+
+    if (!strcmp(readonly, nothing_val)) {
+        EXPECT_FALSE(__android_log_security());
+        fprintf(stderr, "Warning, setting ro.device_owner to a domain\n");
+        property_set(readonly_key, "com.google.android.SecOps.DeviceOwner");
+    } else if (!strcasecmp(readonly, "false") || !readonly[0]) {
+        EXPECT_FALSE(__android_log_security());
+        return;
+    }
+
+    if (!strcasecmp(persist, "true")) {
+        EXPECT_TRUE(__android_log_security());
+    } else {
+        EXPECT_FALSE(__android_log_security());
+    }
+    property_set(persist_key, "TRUE");
+    EXPECT_TRUE(__android_log_security());
+    property_set(persist_key, "FALSE");
+    EXPECT_FALSE(__android_log_security());
+    property_set(persist_key, "true");
+    EXPECT_TRUE(__android_log_security());
+    property_set(persist_key, "false");
+    EXPECT_FALSE(__android_log_security());
+    property_set(persist_key, "");
+    EXPECT_FALSE(__android_log_security());
+    property_set(persist_key, persist);
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, __security_buffer) {
+#ifdef __ANDROID__
     struct logger_list *logger_list;
+    android_event_long_t buffer;
+
+    static const char persist_key[] = "persist.logd.security";
+    char persist[PROP_VALUE_MAX];
+    bool set_persist = false;
+    bool allow_security = false;
+
+    if (__android_log_security()) {
+        allow_security = true;
+    } else {
+        property_get(persist_key, persist, "");
+        if (strcasecmp(persist, "true")) {
+            property_set(persist_key, "TRUE");
+            if (__android_log_security()) {
+                allow_security = true;
+                set_persist = true;
+            } else {
+                property_set(persist_key, persist);
+            }
+        }
+    }
+
+    if (!allow_security) {
+        fprintf(stderr, "WARNING: "
+                "security buffer disabled, bypassing end-to-end test\n");
+
+        log_time ts(CLOCK_MONOTONIC);
+
+        buffer.type = EVENT_TYPE_LONG;
+        buffer.data = *(static_cast<uint64_t *>((void *)&ts));
+
+        // expect failure!
+        ASSERT_GE(0, __android_log_security_bwrite(0, &buffer, sizeof(buffer)));
+
+        return;
+    }
+
+    /* Matches clientHasLogCredentials() in logd */
+    uid_t uid = getuid();
+    gid_t gid = getgid();
+    bool clientHasLogCredentials = true;
+    if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)
+     && (gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
+        uid_t euid = geteuid();
+        if ((euid != AID_SYSTEM) && (euid != AID_ROOT) && (euid != AID_LOG)) {
+            gid_t egid = getegid();
+            if ((egid != AID_SYSTEM) && (egid != AID_ROOT) && (egid != AID_LOG)) {
+                int num_groups = getgroups(0, NULL);
+                if (num_groups > 0) {
+                    gid_t groups[num_groups];
+                    num_groups = getgroups(num_groups, groups);
+                    while (num_groups > 0) {
+                        if (groups[num_groups - 1] == AID_LOG) {
+                            break;
+                        }
+                        --num_groups;
+                    }
+                }
+                if (num_groups <= 0) {
+                    clientHasLogCredentials = false;
+                }
+            }
+        }
+    }
+    if (!clientHasLogCredentials) {
+        fprintf(stderr, "WARNING: "
+                "not in system context, bypassing end-to-end test\n");
+
+        log_time ts(CLOCK_MONOTONIC);
+
+        buffer.type = EVENT_TYPE_LONG;
+        buffer.data = *(static_cast<uint64_t *>((void *)&ts));
+
+        // expect failure!
+        ASSERT_GE(0, __android_log_security_bwrite(0, &buffer, sizeof(buffer)));
+
+        return;
+    }
+
+    EXPECT_EQ(0, setuid(AID_SYSTEM)); // only one that can read security buffer
 
     pid_t pid = getpid();
 
     ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
-        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+        LOG_ID_SECURITY, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+        1000, pid)));
 
-    ASSERT_LT(0, android_errorWriteWithInfoLog(
-            TAG, SUBTAG, UID, max_payload_buf, DATA_LEN));
+    log_time ts(CLOCK_MONOTONIC);
 
-    sleep(2);
+    buffer.type = EVENT_TYPE_LONG;
+    buffer.data = *(static_cast<uint64_t *>((void *)&ts));
+
+    ASSERT_LT(0, __android_log_security_bwrite(0, &buffer, sizeof(buffer)));
+    usleep(1000000);
 
     int count = 0;
 
@@ -1788,88 +1893,68 @@
             break;
         }
 
-        char *eventData = log_msg.msg();
-        if (!eventData) {
+        ASSERT_EQ(log_msg.entry.pid, pid);
+
+        if ((log_msg.entry.len != sizeof(android_log_event_long_t))
+         || (log_msg.id() != LOG_ID_SECURITY)) {
             continue;
         }
 
-        // Tag
-        int tag = get4LE(eventData);
-        eventData += 4;
+        android_log_event_long_t* eventData;
+        eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
 
-        if (tag != TAG) {
+        if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
             continue;
         }
 
-        // List type
-        ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
-        eventData++;
-
-        // Number of elements in list
-        ASSERT_EQ(3, eventData[0]);
-        eventData++;
-
-        // Element #1: string type for subtag
-        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
-        eventData++;
-
-        ASSERT_EQ((int) strlen(SUBTAG), get4LE(eventData));
-        eventData +=4;
-
-        if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) {
-            continue;
+        log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
+        if (ts == tx) {
+            ++count;
         }
-        eventData += strlen(SUBTAG);
-
-        // Element #2: int type for uid
-        ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
-        eventData++;
-
-        ASSERT_EQ(UID, get4LE(eventData));
-        eventData += 4;
-
-        // Element #3: string type for data
-        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
-        eventData++;
-
-        ASSERT_EQ(DATA_LEN, get4LE(eventData));
-        eventData += 4;
-
-        if (memcmp(max_payload_buf, eventData, DATA_LEN)) {
-            continue;
-        }
-
-        ++count;
     }
 
-    EXPECT_EQ(1, count);
+    if (set_persist) {
+        property_set(persist_key, persist);
+    }
 
     android_logger_list_close(logger_list);
+
+    bool clientHasSecurityCredentials = (uid == AID_SYSTEM) || (gid == AID_SYSTEM);
+    if (!clientHasSecurityCredentials) {
+        fprintf(stderr, "WARNING: "
+                "not system, content submitted but can not check end-to-end\n");
+    }
+    EXPECT_EQ(clientHasSecurityCredentials ? 1 : 0, count);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
+#endif // !USING_LOGGER_LOCAL
 
-TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__data_too_large) {
-#ifdef __ANDROID__
-    const int TAG = 123456782;
-    const char SUBTAG[] = "test-subtag";
-    const int UID = -1;
-    const int DATA_LEN = sizeof(max_payload_buf);
+#ifdef TEST_PREFIX
+static void android_errorWriteWithInfoLog_helper(int TAG, const char* SUBTAG,
+                                                 int UID, const char* payload,
+                                                 int DATA_LEN, int& count) {
+    TEST_PREFIX
     struct logger_list *logger_list;
 
     pid_t pid = getpid();
 
+    count = 0;
+
     ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
         LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
 
-    ASSERT_LT(0, android_errorWriteWithInfoLog(
-            TAG, SUBTAG, UID, max_payload_buf, DATA_LEN));
+    int retval_android_errorWriteWithinInfoLog = android_errorWriteWithInfoLog(
+            TAG, SUBTAG, UID, payload, DATA_LEN);
+    if (payload) {
+        ASSERT_LT(0, retval_android_errorWriteWithinInfoLog);
+    } else {
+        ASSERT_GT(0, retval_android_errorWriteWithinInfoLog);
+    }
 
     sleep(2);
 
-    int count = 0;
-
     for (;;) {
         log_msg log_msg;
         if (android_logger_list_read(logger_list, &log_msg) <= 0) {
@@ -1891,6 +1976,12 @@
             continue;
         }
 
+        if (!payload) {
+            // This tag should not have been written because the data was null
+            ++count;
+            break;
+        }
+
         // List type
         ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
         eventData++;
@@ -1903,19 +1994,21 @@
         ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
         eventData++;
 
-        ASSERT_EQ((int) strlen(SUBTAG), get4LE(eventData));
-        eventData +=4;
+        unsigned subtag_len = strlen(SUBTAG);
+        if (subtag_len > 32) subtag_len = 32;
+        ASSERT_EQ(subtag_len, get4LE(eventData));
+        eventData += 4;
 
-        if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) {
+        if (memcmp(SUBTAG, eventData, subtag_len)) {
             continue;
         }
-        eventData += strlen(SUBTAG);
+        eventData += subtag_len;
 
         // Element #2: int type for uid
         ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
         eventData++;
 
-        ASSERT_EQ(UID, get4LE(eventData));
+        ASSERT_EQ(UID, (int)get4LE(eventData));
         eventData += 4;
 
         // Element #3: string type for data
@@ -1924,161 +2017,85 @@
 
         size_t dataLen = get4LE(eventData);
         eventData += 4;
+        if (DATA_LEN < 512) ASSERT_EQ(DATA_LEN, (int)dataLen);
 
-        if (memcmp(max_payload_buf, eventData, dataLen)) {
+        if (memcmp(payload, eventData, dataLen)) {
             continue;
         }
-        eventData += dataLen;
 
-        // 4 bytes for the tag, and max_payload_buf should be truncated.
-        ASSERT_LE(4 + 512, eventData - original);      // worst expectations
-        ASSERT_GT(4 + DATA_LEN, eventData - original); // must be truncated
+        if (DATA_LEN >= 512) {
+            eventData += dataLen;
+            // 4 bytes for the tag, and max_payload_buf should be truncated.
+            ASSERT_LE(4 + 512, eventData - original);      // worst expectations
+            ASSERT_GT(4 + DATA_LEN, eventData - original); // must be truncated
+        }
 
         ++count;
     }
 
-    EXPECT_EQ(1, count);
-
     android_logger_list_close(logger_list);
+}
+#endif
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__typical) {
+#ifdef TEST_PREFIX
+    int count;
+    android_errorWriteWithInfoLog_helper(
+            123456781,
+            "test-subtag",
+            -1,
+            max_payload_buf,
+            200,
+            count);
+    EXPECT_EQ(1, count);
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__data_too_large) {
+#ifdef TEST_PREFIX
+    int count;
+    android_errorWriteWithInfoLog_helper(
+            123456782,
+            "test-subtag",
+            -1,
+            max_payload_buf,
+            sizeof(max_payload_buf),
+            count);
+    EXPECT_EQ(1, count);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
 TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__null_data) {
-#ifdef __ANDROID__
-    const int TAG = 123456783;
-    const char SUBTAG[] = "test-subtag";
-    const int UID = -1;
-    const int DATA_LEN = 200;
-    struct logger_list *logger_list;
-
-    pid_t pid = getpid();
-
-    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
-        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
-    ASSERT_GT(0, android_errorWriteWithInfoLog(
-            TAG, SUBTAG, UID, NULL, DATA_LEN));
-
-    sleep(2);
-
-    int count = 0;
-
-    for (;;) {
-        log_msg log_msg;
-        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
-            break;
-        }
-
-        char *eventData = log_msg.msg();
-        if (!eventData) {
-            continue;
-        }
-
-        // Tag
-        int tag = get4LE(eventData);
-        eventData += 4;
-
-        if (tag == TAG) {
-            // This tag should not have been written because the data was null
-            count++;
-            break;
-        }
-    }
-
+#ifdef TEST_PREFIX
+    int count;
+    android_errorWriteWithInfoLog_helper(
+            123456783,
+            "test-subtag",
+            -1,
+            NULL,
+            200,
+            count);
     EXPECT_EQ(0, count);
-
-    android_logger_list_close(logger_list);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
 TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__subtag_too_long) {
-#ifdef __ANDROID__
-    const int TAG = 123456784;
-    const char SUBTAG[] = "abcdefghijklmnopqrstuvwxyz now i know my abc";
-    const int UID = -1;
-    const int DATA_LEN = 200;
-    struct logger_list *logger_list;
-
-    pid_t pid = getpid();
-
-    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
-        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
-    ASSERT_LT(0, android_errorWriteWithInfoLog(
-            TAG, SUBTAG, UID, max_payload_buf, DATA_LEN));
-
-    sleep(2);
-
-    int count = 0;
-
-    for (;;) {
-        log_msg log_msg;
-        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
-            break;
-        }
-
-        char *eventData = log_msg.msg();
-        if (!eventData) {
-            continue;
-        }
-
-        // Tag
-        int tag = get4LE(eventData);
-        eventData += 4;
-
-        if (tag != TAG) {
-            continue;
-        }
-
-        // List type
-        ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
-        eventData++;
-
-        // Number of elements in list
-        ASSERT_EQ(3, eventData[0]);
-        eventData++;
-
-        // Element #1: string type for subtag
-        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
-        eventData++;
-
-        // The subtag is longer than 32 and should be truncated to that.
-        ASSERT_EQ(32, get4LE(eventData));
-        eventData +=4;
-
-        if (memcmp(SUBTAG, eventData, 32)) {
-            continue;
-        }
-        eventData += 32;
-
-        // Element #2: int type for uid
-        ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
-        eventData++;
-
-        ASSERT_EQ(UID, get4LE(eventData));
-        eventData += 4;
-
-        // Element #3: string type for data
-        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
-        eventData++;
-
-        ASSERT_EQ(DATA_LEN, get4LE(eventData));
-        eventData += 4;
-
-        if (memcmp(max_payload_buf, eventData, DATA_LEN)) {
-            continue;
-        }
-
-        ++count;
-    }
-
+#ifdef TEST_PREFIX
+    int count;
+    android_errorWriteWithInfoLog_helper(
+            123456784,
+            "abcdefghijklmnopqrstuvwxyz now i know my abc",
+            -1,
+            max_payload_buf,
+            200,
+            count);
     EXPECT_EQ(1, count);
-
-    android_logger_list_close(logger_list);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
@@ -2092,23 +2109,27 @@
     buf_write_test(max_payload_buf);
 }
 
-TEST(liblog, android_errorWriteLog__android_logger_list_read__success) {
-#ifdef __ANDROID__
-    const int TAG = 123456785;
-    const char SUBTAG[] = "test-subtag";
+#ifdef TEST_PREFIX
+static void android_errorWriteLog_helper(int TAG, const char *SUBTAG, int& count) {
+    TEST_PREFIX
     struct logger_list *logger_list;
 
     pid_t pid = getpid();
 
+    count = 0;
+
     ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
         LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
 
-    ASSERT_LT(0, android_errorWriteLog(TAG, SUBTAG));
+    int retval_android_errorWriteLog = android_errorWriteLog(TAG, SUBTAG);
+    if (SUBTAG) {
+        ASSERT_LT(0, retval_android_errorWriteLog);
+    } else {
+        ASSERT_GT(0, retval_android_errorWriteLog);
+    }
 
     sleep(2);
 
-    int count = 0;
-
     for (;;) {
         log_msg log_msg;
         if (android_logger_list_read(logger_list, &log_msg) <= 0) {
@@ -2128,6 +2149,12 @@
             continue;
         }
 
+        if (!SUBTAG) {
+            // This tag should not have been written because the data was null
+            ++count;
+            break;
+        }
+
         // List type
         ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
         eventData++;
@@ -2140,7 +2167,7 @@
         ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
         eventData++;
 
-        ASSERT_EQ((int) strlen(SUBTAG), get4LE(eventData));
+        ASSERT_EQ(strlen(SUBTAG), get4LE(eventData));
         eventData +=4;
 
         if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) {
@@ -2149,61 +2176,32 @@
         ++count;
     }
 
-    EXPECT_EQ(1, count);
-
     android_logger_list_close(logger_list);
+}
+#endif
+
+TEST(liblog, android_errorWriteLog__android_logger_list_read__success) {
+#ifdef TEST_PREFIX
+    int count;
+    android_errorWriteLog_helper(123456785, "test-subtag", count);
+    EXPECT_EQ(1, count);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
 TEST(liblog, android_errorWriteLog__android_logger_list_read__null_subtag) {
-#ifdef __ANDROID__
-    const int TAG = 123456786;
-    struct logger_list *logger_list;
-
-    pid_t pid = getpid();
-
-    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
-        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
-    ASSERT_GT(0, android_errorWriteLog(TAG, NULL));
-
-    sleep(2);
-
-    int count = 0;
-
-    for (;;) {
-        log_msg log_msg;
-        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
-            break;
-        }
-
-        char *eventData = log_msg.msg();
-        if (!eventData) {
-            continue;
-        }
-
-        // Tag
-        int tag = get4LE(eventData);
-        eventData += 4;
-
-        if (tag == TAG) {
-            // This tag should not have been written because the data was null
-            count++;
-            break;
-        }
-    }
-
+#ifdef TEST_PREFIX
+    int count;
+    android_errorWriteLog_helper(123456786, NULL, count);
     EXPECT_EQ(0, count);
-
-    android_logger_list_close(logger_list);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
-#ifdef __ANDROID__
+// Do not retest logger list handling
+#if (defined(TEST_PREFIX) || !defined(USING_LOGGER_LOCAL))
 static int is_real_element(int type) {
     return ((type == EVENT_TYPE_INT) ||
             (type == EVENT_TYPE_LONG) ||
@@ -2211,8 +2209,8 @@
             (type == EVENT_TYPE_FLOAT));
 }
 
-int android_log_buffer_to_string(const char *msg, size_t len,
-                                 char *strOut, size_t strOutLen) {
+static int android_log_buffer_to_string(const char *msg, size_t len,
+                                        char *strOut, size_t strOutLen) {
     android_log_context context = create_android_log_parser(msg, len);
     android_log_list_element elem;
     bool overflow = false;
@@ -2362,7 +2360,9 @@
 
     return 0;
 }
+#endif // TEST_PREFIX || !USING_LOGGER_LOCAL
 
+#ifdef TEST_PREFIX
 static const char *event_test_int32(uint32_t tag, size_t &expected_len) {
     android_log_context ctx;
 
@@ -2618,6 +2618,7 @@
 }
 
 static void create_android_logger(const char *(*fn)(uint32_t tag, size_t &expected_len)) {
+    TEST_PREFIX
     struct logger_list *logger_list;
 
     pid_t pid = getpid();
@@ -2625,7 +2626,11 @@
     ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
         LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
 
+#ifdef __ANDROID__
     log_time ts(android_log_clockid());
+#else
+    log_time ts(CLOCK_REALTIME);
+#endif
 
     size_t expected_len;
     const char *expected_string = (*fn)(1005, expected_len);
@@ -2666,18 +2671,23 @@
             &log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
         EXPECT_EQ(0, processBinaryLogBuffer);
         if (processBinaryLogBuffer == 0) {
+            int line_overhead = 20;
+            if (pid > 99999) ++line_overhead;
+            if (pid > 999999) ++line_overhead;
             print_barrier();
             int printLogLine = android_log_printLogLine(
                 logformat, fileno(stderr), &entry);
             print_barrier();
-            EXPECT_EQ(20 + (int)strlen(expected_string), printLogLine);
+            EXPECT_EQ(line_overhead + (int)strlen(expected_string),
+                      printLogLine);
         }
         android_log_format_free(logformat);
 
         // test buffer reading API
         int buffer_to_string = -1;
         if (eventData) {
-            snprintf(msgBuf, sizeof(msgBuf), "I/[%d]", get4LE(eventData));
+            snprintf(msgBuf, sizeof(msgBuf),
+                     "I/[%" PRIu32 "]", get4LE(eventData));
             print_barrier();
             fprintf(stderr, "%-10s(%5u): ", msgBuf, pid);
             memset(msgBuf, 0, sizeof(msgBuf));
@@ -2700,7 +2710,7 @@
 #endif
 
 TEST(liblog, create_android_logger_int32) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
     create_android_logger(event_test_int32);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2708,7 +2718,7 @@
 }
 
 TEST(liblog, create_android_logger_int64) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
     create_android_logger(event_test_int64);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2716,7 +2726,7 @@
 }
 
 TEST(liblog, create_android_logger_list_int64) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
     create_android_logger(event_test_list_int64);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2724,7 +2734,7 @@
 }
 
 TEST(liblog, create_android_logger_simple_automagic_list) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
     create_android_logger(event_test_simple_automagic_list);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2732,7 +2742,7 @@
 }
 
 TEST(liblog, create_android_logger_list_empty) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
     create_android_logger(event_test_list_empty);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2740,7 +2750,7 @@
 }
 
 TEST(liblog, create_android_logger_complex_nested_list) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
     create_android_logger(event_test_complex_nested_list);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2748,7 +2758,7 @@
 }
 
 TEST(liblog, create_android_logger_7_level_prefix) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
     create_android_logger(event_test_7_level_prefix);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2756,7 +2766,7 @@
 }
 
 TEST(liblog, create_android_logger_7_level_suffix) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
     create_android_logger(event_test_7_level_suffix);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2764,7 +2774,7 @@
 }
 
 TEST(liblog, create_android_logger_android_log_error_write) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
     create_android_logger(event_test_android_log_error_write);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2772,15 +2782,15 @@
 }
 
 TEST(liblog, create_android_logger_android_log_error_write_null) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
     create_android_logger(event_test_android_log_error_write_null);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
+#ifndef USING_LOGGER_LOCAL // Do not retest logger list handling
 TEST(liblog, create_android_logger_overflow) {
-#ifdef __ANDROID__
     android_log_context ctx;
 
     EXPECT_TRUE(NULL != (ctx = create_android_logger(1005)));
@@ -2805,13 +2815,9 @@
     EXPECT_GT(0, android_log_write_list_begin(ctx));
     EXPECT_LE(0, android_log_destroy(&ctx));
     ASSERT_TRUE(NULL == ctx);
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
 }
 
 TEST(liblog, android_log_write_list_buffer) {
-#ifdef __ANDROID__
     __android_log_event_list ctx(1005);
     ctx << 1005 << "tag_def" << "(tag|1),(name|3),(format|3)";
     std::string buffer(ctx);
@@ -2822,11 +2828,10 @@
     EXPECT_EQ(android_log_buffer_to_string(buffer.data(), buffer.length(),
                                            msgBuf, sizeof(msgBuf)), 0);
     EXPECT_STREQ(msgBuf, "[1005,tag_def,(tag|1),(name|3),(format|3)]");
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
 }
+#endif // !USING_LOGGER_LOCAL
 
+#ifndef USING_LOGGER_LOCAL // Do not retest pmsg functionality
 #ifdef __ANDROID__
 static const char __pmsg_file[] =
         "/data/william-shakespeare/MuchAdoAboutNothing.txt";
@@ -2835,10 +2840,15 @@
 TEST(liblog, __android_log_pmsg_file_write) {
 #ifdef __ANDROID__
     __android_log_close();
-    bool pmsgActiveAfter__android_log_close = isPmsgActive();
-    bool logdwActiveAfter__android_log_close = isLogdwActive();
-    EXPECT_FALSE(pmsgActiveAfter__android_log_close);
-    EXPECT_FALSE(logdwActiveAfter__android_log_close);
+    if (getuid() == AID_ROOT) {
+        tested__android_log_close = true;
+        bool pmsgActiveAfter__android_log_close = isPmsgActive();
+        bool logdwActiveAfter__android_log_close = isLogdwActive();
+        EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+        EXPECT_FALSE(logdwActiveAfter__android_log_close);
+    } else if (!tested__android_log_close) {
+        fprintf(stderr, "WARNING: can not test __android_log_close()\n");
+    }
     int return__android_log_pmsg_file_write = __android_log_pmsg_file_write(
             LOG_ID_CRASH, ANDROID_LOG_VERBOSE,
             __pmsg_file, max_payload_buf, sizeof(max_payload_buf));
@@ -2852,32 +2862,40 @@
                         "with liblog.__android_log_msg_file_read test\n",
                         __pmsg_file);
     }
-    bool pmsgActiveAfter__android_pmsg_file_write = isPmsgActive();
-    bool logdwActiveAfter__android_pmsg_file_write = isLogdwActive();
-    EXPECT_FALSE(pmsgActiveAfter__android_pmsg_file_write);
-    EXPECT_FALSE(logdwActiveAfter__android_pmsg_file_write);
+    bool pmsgActiveAfter__android_pmsg_file_write;
+    bool logdwActiveAfter__android_pmsg_file_write;
+    if (getuid() == AID_ROOT) {
+        pmsgActiveAfter__android_pmsg_file_write = isPmsgActive();
+        logdwActiveAfter__android_pmsg_file_write = isLogdwActive();
+        EXPECT_FALSE(pmsgActiveAfter__android_pmsg_file_write);
+        EXPECT_FALSE(logdwActiveAfter__android_pmsg_file_write);
+    }
     EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
                                          "TEST__android_log_pmsg_file_write",
                                          "main"));
-    bool pmsgActiveAfter__android_log_buf_print = isPmsgActive();
-    bool logdwActiveAfter__android_log_buf_print = isLogdwActive();
-    EXPECT_TRUE(pmsgActiveAfter__android_log_buf_print);
-    EXPECT_TRUE(logdwActiveAfter__android_log_buf_print);
+    if (getuid() == AID_ROOT) {
+        bool pmsgActiveAfter__android_log_buf_print = isPmsgActive();
+        bool logdwActiveAfter__android_log_buf_print = isLogdwActive();
+        EXPECT_TRUE(pmsgActiveAfter__android_log_buf_print);
+        EXPECT_TRUE(logdwActiveAfter__android_log_buf_print);
+    }
     EXPECT_LT(0, __android_log_pmsg_file_write(
             LOG_ID_CRASH, ANDROID_LOG_VERBOSE,
             __pmsg_file, max_payload_buf, sizeof(max_payload_buf)));
-    pmsgActiveAfter__android_pmsg_file_write = isPmsgActive();
-    logdwActiveAfter__android_pmsg_file_write = isLogdwActive();
-    EXPECT_TRUE(pmsgActiveAfter__android_pmsg_file_write);
-    EXPECT_TRUE(logdwActiveAfter__android_pmsg_file_write);
+    if (getuid() == AID_ROOT) {
+        pmsgActiveAfter__android_pmsg_file_write = isPmsgActive();
+        logdwActiveAfter__android_pmsg_file_write = isLogdwActive();
+        EXPECT_TRUE(pmsgActiveAfter__android_pmsg_file_write);
+        EXPECT_TRUE(logdwActiveAfter__android_pmsg_file_write);
+    }
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
 #ifdef __ANDROID__
-ssize_t __pmsg_fn(log_id_t logId, char prio, const char *filename,
-                  const char *buf, size_t len, void *arg) {
+static ssize_t __pmsg_fn(log_id_t logId, char prio, const char *filename,
+                         const char *buf, size_t len, void *arg) {
     EXPECT_TRUE(NULL == arg);
     EXPECT_EQ(LOG_ID_CRASH, logId);
     EXPECT_EQ(ANDROID_LOG_VERBOSE, prio);
@@ -2904,19 +2922,26 @@
     signaled = 0;
 
     __android_log_close();
-    bool pmsgActiveAfter__android_log_close = isPmsgActive();
-    bool logdwActiveAfter__android_log_close = isLogdwActive();
-    EXPECT_FALSE(pmsgActiveAfter__android_log_close);
-    EXPECT_FALSE(logdwActiveAfter__android_log_close);
+    if (getuid() == AID_ROOT) {
+        tested__android_log_close = true;
+        bool pmsgActiveAfter__android_log_close = isPmsgActive();
+        bool logdwActiveAfter__android_log_close = isLogdwActive();
+        EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+        EXPECT_FALSE(logdwActiveAfter__android_log_close);
+    } else if (!tested__android_log_close) {
+        fprintf(stderr, "WARNING: can not test __android_log_close()\n");
+    }
 
     ssize_t ret = __android_log_pmsg_file_read(
             LOG_ID_CRASH, ANDROID_LOG_VERBOSE,
             __pmsg_file, __pmsg_fn, NULL);
 
-    bool pmsgActiveAfter__android_log_pmsg_file_read = isPmsgActive();
-    bool logdwActiveAfter__android_log_pmsg_file_read = isLogdwActive();
-    EXPECT_FALSE(pmsgActiveAfter__android_log_pmsg_file_read);
-    EXPECT_FALSE(logdwActiveAfter__android_log_pmsg_file_read);
+    if (getuid() == AID_ROOT) {
+        bool pmsgActiveAfter__android_log_pmsg_file_read = isPmsgActive();
+        bool logdwActiveAfter__android_log_pmsg_file_read = isLogdwActive();
+        EXPECT_FALSE(pmsgActiveAfter__android_log_pmsg_file_read);
+        EXPECT_FALSE(logdwActiveAfter__android_log_pmsg_file_read);
+    }
 
     if (ret == -ENOENT) {
         fprintf(stderr,
@@ -2932,15 +2957,10 @@
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
+#endif // !USING_LOGGER_LOCAL
 
+#ifndef USING_LOGGER_LOCAL // Do not retest event mapping functionality
 #ifdef __ANDROID__
-// meant to be handed to ASSERT_TRUE / EXPECT_TRUE only to expand the message
-static testing::AssertionResult IsOk(bool ok, std::string &message) {
-    return ok ?
-        testing::AssertionSuccess() :
-        (testing::AssertionFailure() << message);
-}
-
 // must be: '<needle:> 0 kB'
 static bool isZero(const std::string &content, std::string::size_type pos,
                    const char* needle) {
@@ -3022,7 +3042,7 @@
     EXPECT_TRUE(IsOk(private_ok, content));
     EXPECT_TRUE(IsOk(anonymous_ok, content));
 }
-#endif
+#endif // __ANDROID__
 
 TEST(liblog, event_log_tags) {
 #ifdef __ANDROID__
@@ -3043,3 +3063,56 @@
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
+#endif // !USING_LOGGER_LOCAL
+
+#ifndef USING_LOGGER_LOCAL // Do not retest ratelimit
+TEST(liblog, __android_log_ratelimit) {
+    time_t state = 0;
+
+    errno = 42;
+    // Prime
+    __android_log_ratelimit(3, &state);
+    EXPECT_EQ(errno, 42);
+    // Check
+    EXPECT_FALSE(__android_log_ratelimit(3, &state));
+    sleep(1);
+    EXPECT_FALSE(__android_log_ratelimit(3, &state));
+    sleep(4);
+    EXPECT_TRUE(__android_log_ratelimit(3, &state));
+    sleep(5);
+    EXPECT_TRUE(__android_log_ratelimit(3, &state));
+
+    // API checks
+    IF_ALOG_RATELIMIT_LOCAL(3, &state) {
+        EXPECT_FALSE(0 != "IF_ALOG_RATELIMIT_LOCAL(3, &state)");
+    }
+
+    IF_ALOG_RATELIMIT() {
+        ;
+    } else {
+        EXPECT_TRUE(0 == "IF_ALOG_RATELIMIT()");
+    }
+    IF_ALOG_RATELIMIT() {
+        EXPECT_FALSE(0 != "IF_ALOG_RATELIMIT()");
+    }
+    // Do not test default seconds, to allow liblog to tune freely
+}
+#endif // !USING_LOGGER_LOCAL
+
+#ifndef USING_LOGGER_LOCAL // Do not retest event mapping functionality
+TEST(liblog, android_lookupEventTagNum) {
+#ifdef __ANDROID__
+    EventTagMap* map = android_openEventTagMap(NULL);
+    EXPECT_TRUE(NULL != map);
+    std::string Name = android::base::StringPrintf("a%d", getpid());
+    int tag = android_lookupEventTagNum(map, Name.c_str(), "(new|1)", ANDROID_LOG_UNKNOWN);
+    android_closeEventTagMap(map);
+    if (tag == -1) system("tail -3 /dev/event-log-tags >&2");
+    EXPECT_NE(-1, tag);
+    EXPECT_NE(0, tag);
+    EXPECT_GT(UINT32_MAX, (unsigned)tag);
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+#endif // !USING_LOGGER_LOCAL
diff --git a/liblog/tests/liblog_test_default.cpp b/liblog/tests/liblog_test_default.cpp
new file mode 100644
index 0000000..079ba07
--- /dev/null
+++ b/liblog/tests/liblog_test_default.cpp
@@ -0,0 +1,5 @@
+#ifdef __ANDROID__
+#include <log/log_frontend.h>
+#define TEST_PREFIX android_set_log_frontend(LOGGER_DEFAULT);
+#endif
+#include "liblog_test.cpp"
diff --git a/liblog/tests/liblog_test_local.cpp b/liblog/tests/liblog_test_local.cpp
new file mode 100644
index 0000000..5f7f645
--- /dev/null
+++ b/liblog/tests/liblog_test_local.cpp
@@ -0,0 +1,4 @@
+#include <log/log_frontend.h>
+#define liblog liblog_local
+#define TEST_PREFIX android_set_log_frontend(LOGGER_LOCAL);
+#include "liblog_test.cpp"
diff --git a/liblog/tests/log_id_test.cpp b/liblog/tests/log_id_test.cpp
new file mode 100644
index 0000000..b8223f1
--- /dev/null
+++ b/liblog/tests/log_id_test.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2013-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 <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+// Test the APIs in this standalone include file
+#include <log/log_id.h>
+
+// We do not want to include <android/log.h> to acquire ANDROID_LOG_INFO for
+// include file API purity.  We do however want to allow the _option_ that
+// log/log_id.h could include this file, or related content, in the future.
+#ifndef __android_LogPriority_defined
+# define ANDROID_LOG_INFO 4
+#endif
+
+TEST(liblog, log_id) {
+    int count = 0;
+
+    for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+        log_id_t id = static_cast<log_id_t>(i);
+        const char *name = android_log_id_to_name(id);
+        if (id != android_name_to_log_id(name)) {
+            continue;
+        }
+        ++count;
+        fprintf(stderr, "log buffer %s\r", name);
+    }
+    ASSERT_EQ(LOG_ID_MAX, count);
+}
+
+TEST(liblog, __android_log_buf_print) {
+    EXPECT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO,
+                                         "TEST__android_log_buf_print",
+                                         "radio"));
+    usleep(1000);
+    EXPECT_LT(0, __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+                                         "TEST__android_log_buf_print",
+                                         "system"));
+    usleep(1000);
+    EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
+                                         "TEST__android_log_buf_print",
+                                         "main"));
+    usleep(1000);
+}
+
+TEST(liblog, __android_log_buf_write) {
+    EXPECT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO,
+                                         "TEST__android_log_buf_write",
+                                         "radio"));
+    usleep(1000);
+    EXPECT_LT(0, __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+                                         "TEST__android_log_buf_write",
+                                         "system"));
+    usleep(1000);
+    EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
+                                         "TEST__android_log_buf_write",
+                                         "main"));
+    usleep(1000);
+}
+
+static void* ConcurrentPrintFn(void *arg) {
+    int ret = __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
+                                  "TEST__android_log_print", "Concurrent %" PRIuPTR,
+                                  reinterpret_cast<uintptr_t>(arg));
+    return reinterpret_cast<void*>(ret);
+}
+
+#define NUM_CONCURRENT 64
+#define _concurrent_name(a,n) a##__concurrent##n
+#define concurrent_name(a,n) _concurrent_name(a,n)
+
+TEST(liblog, concurrent_name(__android_log_buf_print, NUM_CONCURRENT)) {
+    pthread_t t[NUM_CONCURRENT];
+    int i;
+    for (i=0; i < NUM_CONCURRENT; i++) {
+        ASSERT_EQ(0, pthread_create(&t[i], NULL,
+                                    ConcurrentPrintFn,
+                                    reinterpret_cast<void *>(i)));
+    }
+    int ret = 0;
+    for (i=0; i < NUM_CONCURRENT; i++) {
+        void* result;
+        ASSERT_EQ(0, pthread_join(t[i], &result));
+        int this_result = reinterpret_cast<uintptr_t>(result);
+        if ((0 == ret) && (0 != this_result)) {
+            ret = this_result;
+        }
+    }
+    ASSERT_LT(0, ret);
+}
diff --git a/liblog/tests/log_radio_test.cpp b/liblog/tests/log_radio_test.cpp
new file mode 100644
index 0000000..ecba777
--- /dev/null
+++ b/liblog/tests/log_radio_test.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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 <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+// Test the APIs in this standalone include file
+#include <log/log_radio.h>
+
+TEST(liblog, RLOG) {
+    static const char content[] = "log_radio.h";
+    static const char content_false[] = "log_radio.h false";
+
+    // ratelimit content to 10/s to keep away from spam filters
+    // do not send identical content together to keep away from spam filters
+
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGV"
+    RLOGV(content);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGD"
+    RLOGD(content);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGI"
+    RLOGI(content);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGW"
+    RLOGW(content);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGE"
+    RLOGE(content);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGV"
+    RLOGV_IF(true, content);
+    usleep(100000);
+    RLOGV_IF(false, content_false);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGD"
+    RLOGD_IF(true, content);
+    usleep(100000);
+    RLOGD_IF(false, content_false);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGI"
+    RLOGI_IF(true, content);
+    usleep(100000);
+    RLOGI_IF(false, content_false);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGW"
+    RLOGW_IF(true, content);
+    usleep(100000);
+    RLOGW_IF(false, content_false);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGE"
+    RLOGE_IF(true, content);
+    usleep(100000);
+    RLOGE_IF(false, content_false);
+
+#ifdef __ANDROID__
+    // give time for content to long-path through logger
+    sleep(1);
+
+    std::string buf = android::base::StringPrintf(
+        "logcat -b radio --pid=%u -d -s"
+            " TEST__RLOGV TEST__RLOGD TEST__RLOGI TEST__RLOGW TEST__RLOGE",
+        (unsigned)getpid());
+    FILE* fp = popen(buf.c_str(), "r");
+    int count = 0;
+    int count_false = 0;
+    if (fp) {
+        if (!android::base::ReadFdToString(fileno(fp), &buf)) buf = "";
+        pclose(fp);
+        for (size_t pos = 0; (pos = buf.find(content, pos)) != std::string::npos; ++pos) {
+            ++count;
+        }
+        for (size_t pos = 0; (pos = buf.find(content_false, pos)) != std::string::npos; ++pos) {
+            ++count_false;
+        }
+    }
+    EXPECT_EQ(0, count_false);
+#if LOG_NDEBUG
+    ASSERT_EQ(8, count);
+#else
+    ASSERT_EQ(10, count);
+#endif
+
+#else
+    GTEST_LOG_(INFO) << "This test does not test end-to-end.\n";
+#endif
+}
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
new file mode 100644
index 0000000..2e02407
--- /dev/null
+++ b/liblog/tests/log_read_test.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2013-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 <time.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android/log.h> // minimal logging API
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+// Test the APIs in this standalone include file
+#include <log/log_read.h>
+// Do not use anything in log/log_time.h despite side effects of the above.
+
+TEST(liblog, __android_log_write__android_logger_list_read) {
+#ifdef __ANDROID__
+    pid_t pid = getpid();
+
+    struct logger_list *logger_list;
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    struct timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    std::string buf = android::base::StringPrintf("pid=%u ts=%ld.%09ld",
+                                                  pid, ts.tv_sec, ts.tv_nsec);
+    static const char tag[] = "liblog.__android_log_write__android_logger_list_read";
+    static const char prio = ANDROID_LOG_DEBUG;
+    ASSERT_LT(0, __android_log_write(prio, tag, buf.c_str()));
+    usleep(1000000);
+
+    buf = std::string(&prio, sizeof(prio)) +
+          tag +
+          std::string("", 1) +
+          buf +
+          std::string("", 1);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
+
+        EXPECT_EQ(log_msg.entry.pid, pid);
+        // There may be a future where we leak "liblog" tagged LOG_ID_EVENT
+        // binary messages through so that logger losses can be correlated?
+        EXPECT_EQ(log_msg.id(), LOG_ID_MAIN);
+
+        if (log_msg.entry.len != buf.length()) continue;
+
+        if (buf != std::string(log_msg.msg(), log_msg.entry.len)) continue;
+
+        ++count;
+    }
+    android_logger_list_close(logger_list);
+
+    EXPECT_EQ(1, count);
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, android_logger_get_) {
+#ifdef __ANDROID__
+    // This test assumes the log buffers are filled with noise from
+    // normal operations. It will fail if done immediately after a
+    // logcat -c.
+    struct logger_list * logger_list = android_logger_list_alloc(ANDROID_LOG_WRONLY, 0, 0);
+
+    for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+        log_id_t id = static_cast<log_id_t>(i);
+        const char *name = android_log_id_to_name(id);
+        if (id != android_name_to_log_id(name)) {
+            continue;
+        }
+        fprintf(stderr, "log buffer %s\r", name);
+        struct logger * logger;
+        EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
+        EXPECT_EQ(id, android_logger_get_id(logger));
+        ssize_t get_log_size = android_logger_get_log_size(logger);
+        /* security buffer is allowed to be denied */
+        if (strcmp("security", name)) {
+            EXPECT_LT(0, get_log_size);
+            /* crash buffer is allowed to be empty, that is actually healthy! */
+            EXPECT_LE((strcmp("crash", name)) != 0,
+                      android_logger_get_log_readable_size(logger));
+        } else {
+            EXPECT_NE(0, get_log_size);
+            if (get_log_size < 0) {
+                EXPECT_GT(0, android_logger_get_log_readable_size(logger));
+            } else {
+                EXPECT_LE(0, android_logger_get_log_readable_size(logger));
+            }
+        }
+        EXPECT_LT(0, android_logger_get_log_version(logger));
+    }
+
+    android_logger_list_close(logger_list);
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
diff --git a/liblog/tests/log_system_test.cpp b/liblog/tests/log_system_test.cpp
new file mode 100644
index 0000000..40e3a63
--- /dev/null
+++ b/liblog/tests/log_system_test.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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 <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+// Test the APIs in this standalone include file
+#include <log/log_system.h>
+
+TEST(liblog, SLOG) {
+    static const char content[] = "log_system.h";
+    static const char content_false[] = "log_system.h false";
+
+    // ratelimit content to 10/s to keep away from spam filters
+    // do not send identical content together to keep away from spam filters
+
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGV"
+    SLOGV(content);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGD"
+    SLOGD(content);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGI"
+    SLOGI(content);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGW"
+    SLOGW(content);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGE"
+    SLOGE(content);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGV"
+    SLOGV_IF(true, content);
+    usleep(100000);
+    SLOGV_IF(false, content_false);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGD"
+    SLOGD_IF(true, content);
+    usleep(100000);
+    SLOGD_IF(false, content_false);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGI"
+    SLOGI_IF(true, content);
+    usleep(100000);
+    SLOGI_IF(false, content_false);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGW"
+    SLOGW_IF(true, content);
+    usleep(100000);
+    SLOGW_IF(false, content_false);
+    usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGE"
+    SLOGE_IF(true, content);
+    usleep(100000);
+    SLOGE_IF(false, content_false);
+
+#ifdef __ANDROID__
+    // give time for content to long-path through logger
+    sleep(1);
+
+    std::string buf = android::base::StringPrintf(
+        "logcat -b system --pid=%u -d -s"
+            " TEST__SLOGV TEST__SLOGD TEST__SLOGI TEST__SLOGW TEST__SLOGE",
+        (unsigned)getpid());
+    FILE* fp = popen(buf.c_str(), "r");
+    int count = 0;
+    int count_false = 0;
+    if (fp) {
+        if (!android::base::ReadFdToString(fileno(fp), &buf)) buf = "";
+        pclose(fp);
+        for (size_t pos = 0; (pos = buf.find(content, pos)) != std::string::npos; ++pos) {
+            ++count;
+        }
+        for (size_t pos = 0; (pos = buf.find(content_false, pos)) != std::string::npos; ++pos) {
+            ++count_false;
+        }
+    }
+    EXPECT_EQ(0, count_false);
+#if LOG_NDEBUG
+    ASSERT_EQ(8, count);
+#else
+    ASSERT_EQ(10, count);
+#endif
+
+#else
+    GTEST_LOG_(INFO) << "This test does not test end-to-end.\n";
+#endif
+}
diff --git a/liblog/tests/log_time_test.cpp b/liblog/tests/log_time_test.cpp
new file mode 100644
index 0000000..59655ba
--- /dev/null
+++ b/liblog/tests/log_time_test.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 <time.h>
+
+#include <gtest/gtest.h>
+// Test the APIs in this standalone include file
+#include <log/log_time.h>
+
+TEST(liblog, log_time) {
+#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+    log_time(CLOCK_MONOTONIC);
+
+    EXPECT_EQ(log_time, log_time::EPOCH);
+#endif
+
+    struct timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    log_time tl(ts);
+
+    EXPECT_EQ(tl, ts);
+    EXPECT_GE(tl, ts);
+    EXPECT_LE(tl, ts);
+}
diff --git a/libmemtrack/memtrack.cpp b/libmemtrack/memtrack.cpp
index 1b38925..c5e74c1 100644
--- a/libmemtrack/memtrack.cpp
+++ b/libmemtrack/memtrack.cpp
@@ -23,7 +23,7 @@
 #include <string.h>
 #include <mutex>
 
-#include <android/log.h>
+#include <log/log.h>
 
 using android::hardware::memtrack::V1_0::IMemtrack;
 using android::hardware::memtrack::V1_0::MemtrackType;
@@ -31,6 +31,7 @@
 using android::hardware::memtrack::V1_0::MemtrackFlag;
 using android::hardware::memtrack::V1_0::MemtrackStatus;
 using android::hardware::hidl_vec;
+using android::hardware::Return;
 
 struct memtrack_proc_type {
     MemtrackType type;
@@ -44,7 +45,7 @@
 
 //TODO(b/31632518)
 static android::sp<IMemtrack> get_instance() {
-    static android::sp<IMemtrack> module = IMemtrack::getService("memtrack");
+    static android::sp<IMemtrack> module = IMemtrack::getService();
     if (module == nullptr) {
         ALOGE("Couldn't load memtrack module");
     }
@@ -69,7 +70,7 @@
     if (memtrack == nullptr)
         return -1;
 
-    memtrack->getMemory(pid, type,
+    Return<void> ret = memtrack->getMemory(pid, type,
         [&t, &err](MemtrackStatus status, hidl_vec<MemtrackRecord> records) {
             if (status != MemtrackStatus::SUCCESS) {
                 err = -1;
@@ -81,7 +82,7 @@
                 t->records[i].flags = records[i].flags;
             }
     });
-    return err;
+    return ret.isOk() ? err : -1;
 }
 
 /* TODO: sanity checks on return values from HALs:
diff --git a/libmemunreachable/log.h b/libmemunreachable/log.h
index dd146b6..cdfbfd9 100644
--- a/libmemunreachable/log.h
+++ b/libmemunreachable/log.h
@@ -19,6 +19,6 @@
 
 #define LOG_TAG "libmemunreachable"
 
-#include <android/log.h>
+#include <log/log.h>
 
 #endif // LIBMEMUNREACHABLE_LOG_H_
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
new file mode 100644
index 0000000..75eab66
--- /dev/null
+++ b/libmetricslogger/Android.bp
@@ -0,0 +1,64 @@
+// Copyright 2017 The Android Open Source Project
+
+metricslogger_lib_src_files = [
+    "metrics_logger.cpp",
+]
+
+cc_defaults {
+    name: "metricslogger_defaults",
+
+    clang: true,
+    host_supported: true,
+
+    export_include_dirs: ["include"],
+    local_include_dirs: ["include"],
+    shared_libs: ["liblog"],
+    whole_static_libs: ["libgtest_prod"],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+
+        // 524291 corresponds to sysui_histogram, from
+        // frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
+        "-DHISTOGRAM_LOG_TAG=524291",
+    ],
+}
+
+// metricslogger shared library
+// -----------------------------------------------------------------------------
+cc_library_shared {
+    name: "libmetricslogger",
+    srcs: metricslogger_lib_src_files,
+    defaults: ["metricslogger_defaults"],
+}
+
+// metricslogger shared library, debug
+// -----------------------------------------------------------------------------
+cc_library_shared {
+    name: "libmetricslogger_debug",
+    srcs: metricslogger_lib_src_files,
+    defaults: ["metricslogger_defaults"],
+
+    target: {
+        host: {
+            cflags: ["-UNDEBUG"],
+        },
+    },
+}
+
+// Native tests
+// -----------------------------------------------------------------------------
+cc_test {
+    name: "metricslogger_tests",
+    defaults: ["metricslogger_defaults"],
+    shared_libs: [
+        "libbase",
+        "libmetricslogger_debug",
+    ],
+    static_libs: ["libBionicGtestMain"],
+    srcs: [
+        "metrics_logger_test.cpp",
+    ],
+}
diff --git a/bootstat/histogram_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
similarity index 72%
rename from bootstat/histogram_logger.h
rename to libmetricslogger/include/metricslogger/metrics_logger.h
index 60c7776..d30e56c 100644
--- a/bootstat/histogram_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,10 +17,12 @@
 #include <cstdint>
 #include <string>
 
-namespace bootstat {
+namespace android {
+namespace metricslogger {
 
-// Builds an EventLog buffer named |event| containing |data| and writes
-// the log into the Tron histogram logs.
+// Logs a Tron histogram metric named |event| containing |data| to the Tron log
+// buffer.
 void LogHistogram(const std::string& event, int32_t data);
 
-}  // namespace bootstat
\ No newline at end of file
+}  // namespace metricslogger
+}  // namespace android
diff --git a/bootstat/histogram_logger.cpp b/libmetricslogger/metrics_logger.cpp
similarity index 76%
rename from bootstat/histogram_logger.cpp
rename to libmetricslogger/metrics_logger.cpp
index 73f3295..f8e0174 100644
--- a/bootstat/histogram_logger.cpp
+++ b/libmetricslogger/metrics_logger.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,19 +14,19 @@
  * limitations under the License.
  */
 
-#include "histogram_logger.h"
+#include "metricslogger/metrics_logger.h"
 
 #include <cstdlib>
 
-#include <android-base/logging.h>
 #include <log/log_event_list.h>
 
-namespace bootstat {
+namespace android {
+namespace metricslogger {
 
 void LogHistogram(const std::string& event, int32_t data) {
-  LOG(INFO) << "Logging histogram: " << event << " " << data;
   android_log_event_list log(HISTOGRAM_LOG_TAG);
   log << event << data << LOG_ID_EVENTS;
 }
 
-}  // namespace bootstat
+}  // namespace metricslogger
+}  // namespace android
diff --git a/bootstat/histogram_logger.h b/libmetricslogger/metrics_logger_test.cpp
similarity index 70%
copy from bootstat/histogram_logger.h
copy to libmetricslogger/metrics_logger_test.cpp
index 60c7776..5a30ad7 100644
--- a/bootstat/histogram_logger.h
+++ b/libmetricslogger/metrics_logger_test.cpp
@@ -14,13 +14,11 @@
  * limitations under the License.
  */
 
-#include <cstdint>
-#include <string>
+#include "metricslogger/metrics_logger.h"
 
-namespace bootstat {
+#include <gtest/gtest.h>
 
-// Builds an EventLog buffer named |event| containing |data| and writes
-// the log into the Tron histogram logs.
-void LogHistogram(const std::string& event, int32_t data);
-
-}  // namespace bootstat
\ No newline at end of file
+TEST(MetricsLoggerTest, AddSingleBootEvent) {
+  android::metricslogger::LogHistogram("test_event", 42);
+  // TODO(jhawkins): Verify the EventLog is updated.
+}
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index eafc53d..83f35b1 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -28,7 +28,7 @@
 
 #include <cstring>
 
-#include <android/log.h>
+#include <log/log.h>
 
 namespace android {
 
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index 9d33899..c1133fb 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -9,8 +9,8 @@
         "liblog",
         "libcutils",
         "libnativebridge",
+        "libbase",
     ],
-    static_libs: ["libbase"],
     target: {
         android: {
             shared_libs: ["libdl"],
diff --git a/libnativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
similarity index 64%
rename from libnativeloader/dlext_namespaces.h
rename to libnativeloader/include/nativeloader/dlext_namespaces.h
index 13a44e2..ac64f71 100644
--- a/libnativeloader/dlext_namespaces.h
+++ b/libnativeloader/include/nativeloader/dlext_namespaces.h
@@ -22,16 +22,15 @@
 __BEGIN_DECLS
 
 /*
- * Initializes public and anonymous namespaces. The public_ns_sonames is the list of sonames
- * to be included into public namespace separated by colon. Example: "libc.so:libm.so:libdl.so".
- * The libraries in this list should be loaded prior to this call.
+ * Initializes anonymous namespaces. The shared_libs_sonames is the list of sonames
+ * to be shared by default namespace separated by colon. Example: "libc.so:libm.so:libdl.so".
  *
- * The anon_ns_library_path is the search path for anonymous namespace. The anonymous namespace
+ * The library_search_path is the search path for anonymous namespace. The anonymous namespace
  * is used in the case when linker cannot identify the caller of dlopen/dlsym. This happens
  * for the code not loaded by dynamic linker; for example calls from the mono-compiled code.
  */
-extern bool android_init_namespaces(const char* public_ns_sonames,
-                                    const char* anon_ns_library_path);
+extern bool android_init_anonymous_namespace(const char* shared_libs_sonames,
+                                             const char* library_search_path);
 
 
 enum {
@@ -86,6 +85,39 @@
                                                             const char* permitted_when_isolated_path,
                                                             android_namespace_t* parent);
 
+/*
+ * Creates a link between namespaces. Every link has list of sonames of
+ * shared libraries. These are the libraries which are accessible from
+ * namespace 'from' but loaded within namespace 'to' context.
+ * When to namespace is nullptr this function establishes a link between
+ * 'from' namespace and the default namespace.
+ *
+ * The lookup order of the libraries in namespaces with links is following:
+ * 1. Look inside current namespace using 'this' namespace search path.
+ * 2. Look in linked namespaces
+ * 2.1. Perform soname check - if library soname is not in the list of shared
+ *      libraries sonames skip this link, otherwise
+ * 2.2. Search library using linked namespace search path. Note that this
+ *      step will not go deeper into linked namespaces for this library but
+ *      will do so for DT_NEEDED libraries.
+ */
+extern bool android_link_namespaces(android_namespace_t* from,
+                                    android_namespace_t* to,
+                                    const char* shared_libs_sonames);
+
+/*
+ * Get the default library search path.
+ * The path will be copied into buffer, which must have space for at least
+ * buffer_size chars. Elements are separated with ':', and the path will always
+ * be null-terminated.
+ *
+ * If buffer_size is too small to hold the entire default search path and the
+ * null terminator, this function will abort. There is currently no way to find
+ * out what the required buffer size is. At the time of this writing, PATH_MAX
+ * is sufficient and used by all callers of this function.
+ */
+extern void android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size);
+
 __END_DECLS
 
 #endif /* __ANDROID_DLEXT_NAMESPACES_H__ */
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 15fe054..74f2f1d 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -19,10 +19,10 @@
 
 #include <dlfcn.h>
 #ifdef __ANDROID__
-#include "dlext_namespaces.h"
 #define LOG_TAG "libnativeloader"
-#include "android/log.h"
+#include "nativeloader/dlext_namespaces.h"
 #include "cutils/properties.h"
+#include "log/log.h"
 #endif
 #include "nativebridge/native_bridge.h"
 
@@ -165,6 +165,11 @@
         return false;
       }
 
+      if (!android_link_namespaces(ns, nullptr, public_libraries_.c_str())) {
+        *error_msg = dlerror();
+        return false;
+      }
+
       native_loader_ns = NativeLoaderNamespace(ns);
     } else {
       native_bridge_namespace_t* ns = NativeBridgeCreateNamespace("classloader-namespace",
@@ -246,7 +251,9 @@
     // For now we rely on CTS test to catch things like this but
     // it should probably be addressed in the future.
     for (const auto& soname : sonames) {
-      dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE);
+      LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
+                          "Error preloading public library %s: %s",
+                          soname.c_str(), dlerror());
     }
 
     public_libraries_ = base::Join(sonames, ':');
@@ -308,8 +315,8 @@
     // code is one example) unknown to linker in which  case linker uses anonymous
     // namespace. The second argument specifies the search path for the anonymous
     // namespace which is the library_path of the classloader.
-    initialized_ = android_init_namespaces(public_libraries_.c_str(),
-                                           is_native_bridge ? nullptr : library_path);
+    initialized_ = android_init_anonymous_namespace(public_libraries_.c_str(),
+                                                    is_native_bridge ? nullptr : library_path);
     if (!initialized_) {
       *error_msg = dlerror();
       return false;
diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c
index d17bdd3..11c116a 100644
--- a/libnetutils/dhcpclient.c
+++ b/libnetutils/dhcpclient.c
@@ -31,8 +31,8 @@
 #include <time.h>
 #include <unistd.h>
 
-#include <android/log.h>
 #include <cutils/properties.h>
+#include <log/log.h>
 
 #include <netutils/ifc.h>
 #include "dhcpmsg.h"
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index 275327a..a098d59 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -38,8 +38,8 @@
 
 #ifdef ANDROID
 #define LOG_TAG "NetUtils"
-#include <android/log.h>
 #include <cutils/properties.h>
+#include <log/log.h>
 #else
 #define ALOGD printf
 #define ALOGW printf
diff --git a/libnetutils/packet.c b/libnetutils/packet.c
index 56168e8..e53a4c8 100644
--- a/libnetutils/packet.c
+++ b/libnetutils/packet.c
@@ -28,7 +28,7 @@
 
 #ifdef ANDROID
 #define LOG_TAG "DHCP"
-#include <android/log.h>
+#include <log/log.h>
 #else
 #include <stdio.h>
 #define ALOGD printf
diff --git a/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
index f74b8b4..3e1a3d1 100644
--- a/libpackagelistparser/packagelistparser.c
+++ b/libpackagelistparser/packagelistparser.c
@@ -27,7 +27,7 @@
 #include <string.h>
 #include <sys/limits.h>
 
-#include <android/log.h>
+#include <log/log.h>
 #include <packagelistparser/packagelistparser.h>
 
 #define CLOGE(fmt, ...) \
diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp
index 36c1326..ac009a9 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.cpp
+++ b/libpixelflinger/codeflinger/ARMAssembler.cpp
@@ -20,8 +20,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include <android/log.h>
 #include <cutils/properties.h>
+#include <log/log.h>
 #include <private/pixelflinger/ggl_context.h>
 
 #include "ARMAssembler.h"
diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp
index e212f1b..c96cf4b 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp
+++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp
@@ -21,7 +21,7 @@
 #include <stdlib.h>
 #include <sys/types.h>
 
-#include <android/log.h>
+#include <log/log.h>
 
 #include "ARMAssemblerInterface.h"
 
diff --git a/libpixelflinger/codeflinger/Arm64Assembler.cpp b/libpixelflinger/codeflinger/Arm64Assembler.cpp
index fb297ec..bff87bb 100644
--- a/libpixelflinger/codeflinger/Arm64Assembler.cpp
+++ b/libpixelflinger/codeflinger/Arm64Assembler.cpp
@@ -32,8 +32,8 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <android/log.h>
 #include <cutils/properties.h>
+#include <log/log.h>
 #include <private/pixelflinger/ggl_context.h>
 
 #include "codeflinger/Arm64Assembler.h"
diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp
index 37bd074..8516640 100644
--- a/libpixelflinger/codeflinger/CodeCache.cpp
+++ b/libpixelflinger/codeflinger/CodeCache.cpp
@@ -23,8 +23,8 @@
 #include <sys/mman.h>
 #include <unistd.h>
 
-#include <android/log.h>
 #include <cutils/ashmem.h>
+#include <log/log.h>
 
 #include "CodeCache.h"
 
diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp
index 0b9b5a4..91fbd53 100644
--- a/libpixelflinger/codeflinger/GGLAssembler.cpp
+++ b/libpixelflinger/codeflinger/GGLAssembler.cpp
@@ -23,7 +23,7 @@
 #include <stdlib.h>
 #include <sys/types.h>
 
-#include <android/log.h>
+#include <log/log.h>
 
 #include "GGLAssembler.h"
 
diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.cpp b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
index a7bbaf7..d5e4cea 100644
--- a/libpixelflinger/codeflinger/MIPS64Assembler.cpp
+++ b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
@@ -30,8 +30,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include <android/log.h>
 #include <cutils/properties.h>
+#include <log/log.h>
 #include <private/pixelflinger/ggl_context.h>
 
 #include "MIPS64Assembler.h"
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.cpp b/libpixelflinger/codeflinger/MIPSAssembler.cpp
index 4cddcc8..865a568 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.cpp
+++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp
@@ -47,14 +47,13 @@
 ** functions in ARMAssemblerInterface.cpp so they could be used as static initializers).
 */
 
-
 #define LOG_TAG "MIPSAssembler"
 
 #include <stdio.h>
 #include <stdlib.h>
 
-#include <android/log.h>
 #include <cutils/properties.h>
+#include <log/log.h>
 #include <private/pixelflinger/ggl_context.h>
 
 #include "CodeCache.h"
diff --git a/libpixelflinger/codeflinger/blending.cpp b/libpixelflinger/codeflinger/blending.cpp
index 092f140..a55dfe3 100644
--- a/libpixelflinger/codeflinger/blending.cpp
+++ b/libpixelflinger/codeflinger/blending.cpp
@@ -23,7 +23,7 @@
 #include <stdlib.h>
 #include <sys/types.h>
 
-#include <android/log.h>
+#include <log/log.h>
 
 #include "GGLAssembler.h"
 
diff --git a/libpixelflinger/codeflinger/load_store.cpp b/libpixelflinger/codeflinger/load_store.cpp
index b8a0e55..da21e1d 100644
--- a/libpixelflinger/codeflinger/load_store.cpp
+++ b/libpixelflinger/codeflinger/load_store.cpp
@@ -20,7 +20,7 @@
 #include <assert.h>
 #include <stdio.h>
 
-#include <android/log.h>
+#include <log/log.h>
 
 #include "GGLAssembler.h"
 
diff --git a/libpixelflinger/codeflinger/texturing.cpp b/libpixelflinger/codeflinger/texturing.cpp
index f4f4657..4c357af 100644
--- a/libpixelflinger/codeflinger/texturing.cpp
+++ b/libpixelflinger/codeflinger/texturing.cpp
@@ -23,7 +23,7 @@
 #include <stdlib.h>
 #include <sys/types.h>
 
-#include <android/log.h>
+#include <log/log.h>
 
 #include "GGLAssembler.h"
 
diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp
index 1a2f6fb..c6cf5bf 100644
--- a/libpixelflinger/scanline.cpp
+++ b/libpixelflinger/scanline.cpp
@@ -22,8 +22,8 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <android/log.h>
 #include <cutils/memory.h>
+#include <log/log.h>
 
 #include "buffer.h"
 #include "scanline.h"
diff --git a/libpixelflinger/trap.cpp b/libpixelflinger/trap.cpp
index fa6338a..234bfdd 100644
--- a/libpixelflinger/trap.cpp
+++ b/libpixelflinger/trap.cpp
@@ -21,8 +21,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include <android/log.h>
 #include <cutils/memory.h>
+#include <log/log.h>
 
 #include "trap.h"
 #include "picker.h"
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 7a6ae8a..dd8b5fd 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -1,7 +1,9 @@
 // Copyright 2010 The Android Open Source Project
 
-cc_defaults {
-    name: "libsparse_defaults",
+cc_library {
+    name: "libsparse",
+    host_supported: true,
+    unique_host_soname: true,
     srcs: [
         "backed_block.c",
         "output_file.c",
@@ -13,32 +15,19 @@
     cflags: ["-Werror"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
-}
-
-cc_library_host_static {
-    name: "libsparse_host",
-    defaults: ["libsparse_defaults"],
-    static_libs: ["libz"],
     target: {
+        host: {
+            shared_libs: ["libz-host"],
+        },
+        android: {
+            shared_libs: ["libz"],
+        },
         windows: {
             enabled: true,
         },
     },
 }
 
-cc_library_shared {
-    name: "libsparse",
-    defaults: ["libsparse_defaults"],
-    shared_libs: ["libz"],
-}
-
-cc_library_static {
-    name: "libsparse_static",
-    host_supported: true,
-    defaults: ["libsparse_defaults"],
-    static_libs: ["libz"],
-}
-
 cc_binary {
     name: "simg2img",
     host_supported: true,
@@ -47,7 +36,7 @@
         "sparse_crc32.c",
     ],
     static_libs: [
-        "libsparse_static",
+        "libsparse",
         "libz",
     ],
 
@@ -59,7 +48,7 @@
     host_supported: true,
     srcs: ["img2simg.c"],
     static_libs: [
-        "libsparse_static",
+        "libsparse",
         "libz",
     ],
 
@@ -70,7 +59,7 @@
     name: "append2simg",
     srcs: ["append2simg.c"],
     static_libs: [
-        "libsparse_static",
+        "libsparse",
         "libz",
     ],
 
diff --git a/libsuspend/autosuspend.c b/libsuspend/autosuspend.c
index 2e1983a..96e1c10 100644
--- a/libsuspend/autosuspend.c
+++ b/libsuspend/autosuspend.c
@@ -18,7 +18,7 @@
 
 #include <stdbool.h>
 
-#include <android/log.h>
+#include <log/log.h>
 
 #include <suspend/autosuspend.h>
 
diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c
index d3fb45f..4dedf7f 100644
--- a/libsuspend/autosuspend_wakeup_count.c
+++ b/libsuspend/autosuspend_wakeup_count.c
@@ -28,7 +28,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <android/log.h>
+#include <log/log.h>
 
 #include "autosuspend_ops.h"
 
diff --git a/libsync/Android.bp b/libsync/Android.bp
index a4e5599..b293da4 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -1,3 +1,17 @@
+ndk_headers {
+    name: "libsync_headers",
+    from: "include/ndk",
+    to: "android",
+    srcs: ["include/ndk/sync.h"],
+    license: "NOTICE",
+}
+
+ndk_library {
+    name: "libsync.ndk",
+    symbol_file: "libsync.map.txt",
+    first_version: "26",
+}
+
 cc_defaults {
     name: "libsync_defaults",
     srcs: ["sync.c"],
diff --git a/libsync/NOTICE b/libsync/NOTICE
new file mode 100644
index 0000000..2c8db73
--- /dev/null
+++ b/libsync/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2012-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.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/libsync/include/android/sync.h b/libsync/include/android/sync.h
new file mode 100644
index 0000000..68f74a0
--- /dev/null
+++ b/libsync/include/android/sync.h
@@ -0,0 +1,69 @@
+/*
+ *  sync.h
+ *
+ *   Copyright 2012 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.
+ */
+
+#ifndef __SYS_CORE_SYNC_H
+#define __SYS_CORE_SYNC_H
+
+/* This file contains the legacy sync interface used by Android platform and
+ * device code. The direct contents will be removed over time as code
+ * transitions to using the updated interface in ndk/sync.h. When this file is
+ * empty other than the ndk/sync.h include, that file will be renamed to
+ * replace this one.
+ *
+ * New code should continue to include this file (#include <android/sync.h>)
+ * instead of ndk/sync.h so the eventual rename is seamless, but should only
+ * use the things declared in ndk/sync.h.
+ *
+ * This file used to be called sync/sync.h, but we renamed to that both the
+ * platform and NDK call it android/sync.h. A symlink from the old name to this
+ * one exists temporarily to avoid having to change all sync clients
+ * simultaneously. It will be removed when they've been updated, and probably
+ * after this change has been delivered to AOSP so that integrations don't
+ * break builds.
+ */
+
+#include "../ndk/sync.h"
+
+__BEGIN_DECLS
+
+struct sync_fence_info_data {
+ uint32_t len;
+ char name[32];
+ int32_t status;
+ uint8_t pt_info[0];
+};
+
+struct sync_pt_info {
+ uint32_t len;
+ char obj_name[32];
+ char driver_name[32];
+ int32_t status;
+ uint64_t timestamp_ns;
+ uint8_t driver_data[0];
+};
+
+/* timeout in msecs */
+int sync_wait(int fd, int timeout);
+struct sync_fence_info_data *sync_fence_info(int fd);
+struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
+                                  struct sync_pt_info *itr);
+void sync_fence_info_free(struct sync_fence_info_data *info);
+
+__END_DECLS
+
+#endif /* __SYS_CORE_SYNC_H */
diff --git a/libsync/include/ndk/sync.h b/libsync/include/ndk/sync.h
new file mode 100644
index 0000000..758a106
--- /dev/null
+++ b/libsync/include/ndk/sync.h
@@ -0,0 +1,89 @@
+/*
+ *  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.
+ */
+
+#ifndef ANDROID_SYNC_H
+#define ANDROID_SYNC_H
+
+#include <stdint.h>
+
+#include <linux/sync_file.h>
+
+__BEGIN_DECLS
+
+#if __ANDROID_API__ >= __ANDROID_API_O__
+
+/* Fences indicate the status of an asynchronous task. They are initially
+ * in unsignaled state (0), and make a one-time transition to either signaled
+ * (1) or error (< 0) state. A sync file is a collection of one or more fences;
+ * the sync file's status is error if any of its fences are in error state,
+ * signaled if all of the child fences are signaled, or unsignaled otherwise.
+ *
+ * Sync files are created by various device APIs in response to submitting
+ * tasks to the device. Standard file descriptor lifetime syscalls like dup()
+ * and close() are used to manage sync file lifetime.
+ *
+ * The poll(), ppoll(), or select() syscalls can be used to wait for the sync
+ * file to change status, or (with a timeout of zero) to check its status.
+ *
+ * The functions below provide a few additional sync-specific operations.
+ */
+
+/**
+ * Merge two sync files.
+ *
+ * This produces a new sync file with the given name which has the union of the
+ * two original sync file's fences; redundant fences may be removed.
+ *
+ * If one of the input sync files is signaled or invalid, then this function
+ * may behave like dup(): the new file descriptor refers to the valid/unsignaled
+ * sync file with its original name, rather than a new sync file.
+ *
+ * The original fences remain valid, and the caller is responsible for closing
+ * them.
+ */
+int32_t sync_merge(const char *name, int32_t fd1, int32_t fd2);
+
+/**
+ * Retrieve detailed information about a sync file and its fences.
+ *
+ * The returned sync_file_info must be freed by calling sync_file_info_free().
+ */
+struct sync_file_info *sync_file_info(int32_t fd);
+
+/**
+ * Get the array of fence infos from the sync file's info.
+ *
+ * The returned array is owned by the parent sync file info, and has
+ * info->num_fences entries.
+ */
+inline struct sync_fence_info *sync_get_fence_info(
+        const struct sync_file_info *info) {
+// This header should compile in C, but some C++ projects enable
+// warnings-as-error for C-style casts.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+    return (struct sync_fence_info *)(uintptr_t)(info->sync_fence_info);
+#pragma GCC diagnostic pop
+}
+
+/** Free a struct sync_file_info structure */
+void sync_file_info_free(struct sync_file_info *info);
+
+#endif // __ANDROID_API__ >= __ANDROID_API_O__
+
+__END_DECLS
+
+#endif /* ANDROID_SYNC_H */
diff --git a/libsync/include/sync/sync.h b/libsync/include/sync/sync.h
deleted file mode 100644
index 2e5d82f..0000000
--- a/libsync/include/sync/sync.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- *  sync.h
- *
- *   Copyright 2012 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.
- */
-
-#ifndef __SYS_CORE_SYNC_H
-#define __SYS_CORE_SYNC_H
-
-#include <sys/cdefs.h>
-#include <stdint.h>
-
-__BEGIN_DECLS
-
-// XXX: These structs are copied from the header "linux/sync.h".
-struct sync_fence_info_data {
- uint32_t len;
- char name[32];
- int32_t status;
- uint8_t pt_info[0];
-};
-
-struct sync_pt_info {
- uint32_t len;
- char obj_name[32];
- char driver_name[32];
- int32_t status;
- uint64_t timestamp_ns;
- uint8_t driver_data[0];
-};
-
-/* timeout in msecs */
-int sync_wait(int fd, int timeout);
-int sync_merge(const char *name, int fd1, int fd2);
-struct sync_fence_info_data *sync_fence_info(int fd);
-struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
-                                  struct sync_pt_info *itr);
-void sync_fence_info_free(struct sync_fence_info_data *info);
-
-__END_DECLS
-
-#endif /* __SYS_CORE_SYNC_H */
diff --git a/libsync/include/sync/sync.h b/libsync/include/sync/sync.h
new file mode 120000
index 0000000..3b17e48
--- /dev/null
+++ b/libsync/include/sync/sync.h
@@ -0,0 +1 @@
+../android/sync.h
\ No newline at end of file
diff --git a/libsync/libsync.map.txt b/libsync/libsync.map.txt
new file mode 100644
index 0000000..daa28ae
--- /dev/null
+++ b/libsync/libsync.map.txt
@@ -0,0 +1,32 @@
+#
+# 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.
+#
+
+LIBSYNC {
+  global:
+    sync_merge; # introduced=26
+    sync_get_fence_info; # introduced=26
+    sync_free_fence_info; # introduced=26
+  local:
+    *;
+};
+
+LIBSYNC_PLATFORM {
+  global:
+    sync_wait;
+    sync_fence_info;
+    sync_pt_info;
+    sync_fence_info_free;
+} LIBSYNC_PLATFORM;
diff --git a/libsync/sync.c b/libsync/sync.c
index 169dc36..27dab83 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -20,61 +20,160 @@
 #include <malloc.h>
 #include <stdint.h>
 #include <string.h>
-
-#include <linux/sw_sync.h>
+#include <errno.h>
+#include <poll.h>
 
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <sync/sync.h>
+#include <android/sync.h>
 
-// The sync code is undergoing a major change. Add enough in to get
-// everything to compile wih the latest uapi headers.
-struct sync_merge_data {
-  int32_t fd2;
-  char name[32];
-  int32_t fence;
+/* Legacy Sync API */
+
+struct sync_legacy_merge_data {
+ int32_t fd2;
+ char name[32];
+ int32_t fence;
 };
 
-#define SYNC_IOC_MAGIC '>'
-#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32)
-#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data)
-#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2, struct sync_fence_info_data)
+/**
+ * DOC: SYNC_IOC_MERGE - merge two fences
+ *
+ * Takes a struct sync_merge_data.  Creates a new fence containing copies of
+ * the sync_pts in both the calling fd and sync_merge_data.fd2.  Returns the
+ * new fence's fd in sync_merge_data.fence
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_MERGE   _IOWR(SYNC_IOC_MAGIC, 1, \
+    struct sync_legacy_merge_data)
+
+/**
+ * DOC: SYNC_IOC_LEGACY_FENCE_INFO - get detailed information on a fence
+ *
+ * Takes a struct sync_fence_info_data with extra space allocated for pt_info.
+ * Caller should write the size of the buffer into len.  On return, len is
+ * updated to reflect the total size of the sync_fence_info_data including
+ * pt_info.
+ *
+ * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence.
+ * To iterate over the sync_pt_infos, use the sync_pt_info.len field.
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_FENCE_INFO  _IOWR(SYNC_IOC_MAGIC, 2,\
+    struct sync_fence_info_data)
+
+/* SW Sync API */
+
+struct sw_sync_create_fence_data {
+  __u32 value;
+  char name[32];
+  __s32 fence;
+};
+
+#define SW_SYNC_IOC_MAGIC 'W'
+#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0, struct sw_sync_create_fence_data)
+#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32)
 
 int sync_wait(int fd, int timeout)
 {
-    __s32 to = timeout;
+    struct pollfd fds;
+    int ret;
 
-    return ioctl(fd, SYNC_IOC_WAIT, &to);
+    if (fd < 0) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    fds.fd = fd;
+    fds.events = POLLIN;
+
+    do {
+        ret = poll(&fds, 1, timeout);
+        if (ret > 0) {
+            if (fds.revents & (POLLERR | POLLNVAL)) {
+                errno = EINVAL;
+                return -1;
+            }
+            return 0;
+        } else if (ret == 0) {
+            errno = ETIME;
+            return -1;
+        }
+    } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
+
+    return ret;
 }
 
 int sync_merge(const char *name, int fd1, int fd2)
 {
+    struct sync_legacy_merge_data legacy_data;
     struct sync_merge_data data;
-    int err;
+    int ret;
 
     data.fd2 = fd2;
     strlcpy(data.name, name, sizeof(data.name));
+    data.flags = 0;
+    data.pad = 0;
 
-    err = ioctl(fd1, SYNC_IOC_MERGE, &data);
-    if (err < 0)
-        return err;
+    ret = ioctl(fd1, SYNC_IOC_MERGE, &data);
+    if (ret < 0 && errno == ENOTTY) {
+        legacy_data.fd2 = fd2;
+        strlcpy(legacy_data.name, name, sizeof(legacy_data.name));
+
+        ret = ioctl(fd1, SYNC_IOC_LEGACY_MERGE, &legacy_data);
+        if (ret < 0)
+            return ret;
+
+        return legacy_data.fence;
+    } else if (ret < 0) {
+        return ret;
+    }
 
     return data.fence;
 }
 
-struct sync_fence_info_data *sync_fence_info(int fd)
+static struct sync_fence_info_data *legacy_sync_fence_info(int fd)
 {
-    struct sync_fence_info_data *info;
+    struct sync_fence_info_data *legacy_info;
+    struct sync_pt_info *legacy_pt_info;
     int err;
 
-    info = malloc(4096);
-    if (info == NULL)
+    legacy_info = malloc(4096);
+    if (legacy_info == NULL)
         return NULL;
 
-    info->len = 4096;
-    err = ioctl(fd, SYNC_IOC_FENCE_INFO, info);
+    legacy_info->len = 4096;
+    err = ioctl(fd, SYNC_IOC_LEGACY_FENCE_INFO, legacy_info);
+    if (err < 0) {
+        free(legacy_info);
+        return NULL;
+    }
+    return legacy_info;
+}
+
+static struct sync_file_info *modern_sync_file_info(int fd)
+{
+    struct sync_file_info local_info;
+    struct sync_file_info *info;
+    int err;
+
+    memset(&local_info, 0, sizeof(local_info));
+    err = ioctl(fd, SYNC_IOC_FILE_INFO, &local_info);
+    if (err < 0)
+        return NULL;
+
+    info = calloc(1, sizeof(struct sync_file_info) +
+                  local_info.num_fences * sizeof(struct sync_fence_info));
+    if (!info)
+        return NULL;
+    info->sync_fence_info = (__u64)(uintptr_t)(info + 1);
+
+    err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
     if (err < 0) {
         free(info);
         return NULL;
@@ -83,6 +182,110 @@
     return info;
 }
 
+static struct sync_fence_info_data *sync_file_info_to_legacy_fence_info(
+    const struct sync_file_info *info)
+{
+    struct sync_fence_info_data *legacy_info;
+    struct sync_pt_info *legacy_pt_info;
+    const struct sync_fence_info *fence_info = sync_get_fence_info(info);
+    const uint32_t num_fences = info->num_fences;
+
+    legacy_info = malloc(4096);
+    if (legacy_info == NULL)
+        return NULL;
+    legacy_info->len = sizeof(*legacy_info) +
+                        num_fences * sizeof(struct sync_pt_info);
+    strlcpy(legacy_info->name, info->name, sizeof(legacy_info->name));
+    legacy_info->status = info->status;
+
+    legacy_pt_info = (struct sync_pt_info *)legacy_info->pt_info;
+    for (uint32_t i = 0; i < num_fences; i++) {
+        legacy_pt_info[i].len = sizeof(*legacy_pt_info);
+        strlcpy(legacy_pt_info[i].obj_name, fence_info[i].obj_name,
+                sizeof(legacy_pt_info->obj_name));
+        strlcpy(legacy_pt_info[i].driver_name, fence_info[i].driver_name,
+                sizeof(legacy_pt_info->driver_name));
+        legacy_pt_info[i].status = fence_info[i].status;
+        legacy_pt_info[i].timestamp_ns = fence_info[i].timestamp_ns;
+    }
+
+    return legacy_info;
+}
+
+static struct sync_file_info* legacy_fence_info_to_sync_file_info(
+                                    struct sync_fence_info_data *legacy_info)
+{
+    struct sync_file_info *info;
+    struct sync_pt_info *pt;
+    struct sync_fence_info *fence;
+    size_t num_fences;
+    int err;
+
+    pt = NULL;
+    num_fences = 0;
+    while ((pt = sync_pt_info(legacy_info, pt)) != NULL)
+        num_fences++;
+
+    info = calloc(1, sizeof(struct sync_file_info) +
+                     num_fences * sizeof(struct sync_fence_info));
+    if (!info) {
+        free(legacy_info);
+        return NULL;
+    }
+    info->sync_fence_info = (__u64)(uintptr_t)(info + 1);
+
+    strlcpy(info->name, legacy_info->name, sizeof(info->name));
+    info->status = legacy_info->status;
+    info->num_fences = num_fences;
+
+    pt = NULL;
+    fence = sync_get_fence_info(info);
+    while ((pt = sync_pt_info(legacy_info, pt)) != NULL) {
+        strlcpy(fence->obj_name, pt->obj_name, sizeof(fence->obj_name));
+        strlcpy(fence->driver_name, pt->driver_name,
+                sizeof(fence->driver_name));
+        fence->status = pt->status;
+        fence->timestamp_ns = pt->timestamp_ns;
+        fence++;
+    }
+
+    return info;
+}
+
+struct sync_fence_info_data *sync_fence_info(int fd)
+{
+    struct sync_fence_info_data *legacy_info;
+
+    legacy_info = legacy_sync_fence_info(fd);
+    if (legacy_info || errno != ENOTTY)
+        return legacy_info;
+
+    struct sync_file_info* file_info;
+    file_info = modern_sync_file_info(fd);
+    if (!file_info)
+        return NULL;
+    legacy_info = sync_file_info_to_legacy_fence_info(file_info);
+    sync_file_info_free(file_info);
+    return legacy_info;
+}
+
+struct sync_file_info* sync_file_info(int32_t fd)
+{
+    struct sync_file_info *info;
+    struct sync_fence_info_data *legacy_info;
+
+    info = modern_sync_file_info(fd);
+    if (info || errno != ENOTTY)
+        return info;
+
+    legacy_info = legacy_sync_fence_info(fd);
+    if (!legacy_info)
+        return NULL;
+    info = legacy_fence_info_to_sync_file_info(legacy_info);
+    sync_fence_info_free(legacy_info);
+    return info;
+}
+
 struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
                                   struct sync_pt_info *itr)
 {
@@ -102,10 +305,21 @@
     free(info);
 }
 
+void sync_file_info_free(struct sync_file_info *info)
+{
+    free(info);
+}
+
 
 int sw_sync_timeline_create(void)
 {
-    return open("/dev/sw_sync", O_RDWR);
+    int ret;
+
+    ret = open("/sys/kernel/debug/sync/sw_sync", O_RDWR);
+    if (ret < 0)
+        ret = open("/dev/sw_sync", O_RDWR);
+
+    return ret;
 }
 
 int sw_sync_timeline_inc(int fd, unsigned count)
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
index 9a5f7d8..f1ffdcf 100644
--- a/libsync/sync_test.c
+++ b/libsync/sync_test.c
@@ -22,7 +22,7 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <sync/sync.h>
+#include <android/sync.h>
 #include "sw_sync.h"
 
 pthread_mutex_t printf_mutex = PTHREAD_MUTEX_INITIALIZER;
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index 2c409dc..f08e97e 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -1,5 +1,5 @@
 #include <gtest/gtest.h>
-#include <sync/sync.h>
+#include <android/sync.h>
 #include <sw_sync.h>
 #include <fcntl.h>
 #include <vector>
@@ -172,20 +172,20 @@
         return sync_wait(m_fd, timeout);
     }
     vector<SyncPointInfo> getInfo() const {
-        struct sync_pt_info *pointInfo = nullptr;
         vector<SyncPointInfo> fenceInfo;
-        sync_fence_info_data *info = sync_fence_info(getFd());
+        struct sync_file_info *info = sync_file_info(getFd());
         if (!info) {
             return fenceInfo;
         }
-        while ((pointInfo = sync_pt_info(info, pointInfo))) {
+        const auto fences = sync_get_fence_info(info);
+        for (uint32_t i = 0; i < info->num_fences; i++) {
             fenceInfo.push_back(SyncPointInfo{
-                pointInfo->driver_name,
-                pointInfo->obj_name,
-                pointInfo->timestamp_ns,
-                pointInfo->status});
+                fences[i].driver_name,
+                fences[i].obj_name,
+                fences[i].timestamp_ns,
+                fences[i].status});
         }
-        sync_fence_info_free(info);
+        sync_file_info_free(info);
         return fenceInfo;
     }
     int getSize() const {
@@ -212,6 +212,32 @@
     }
 };
 
+static void CheckModernLegacyInfoMatch(const SyncFence& f) {
+    struct sync_file_info* modern = sync_file_info(f.getFd());
+    struct sync_fence_info_data* legacy = sync_fence_info(f.getFd());
+
+    ASSERT_TRUE(modern != NULL);
+    ASSERT_TRUE(legacy != NULL);
+
+    EXPECT_STREQ(modern->name, legacy->name);
+    EXPECT_EQ(modern->status, legacy->status);
+
+    uint32_t fenceIdx = 0;
+    struct sync_pt_info* pt = sync_pt_info(legacy, NULL);
+    const struct sync_fence_info* fences = sync_get_fence_info(modern);
+    while (fenceIdx < modern->num_fences && pt != NULL) {
+        EXPECT_STREQ(fences[fenceIdx].obj_name, pt->obj_name);
+        EXPECT_STREQ(fences[fenceIdx].driver_name, pt->driver_name);
+        EXPECT_EQ(fences[fenceIdx].status, pt->status);
+        EXPECT_EQ(fences[fenceIdx].timestamp_ns, pt->timestamp_ns);
+
+        fenceIdx++;
+        pt = sync_pt_info(legacy, pt);
+    }
+    EXPECT_EQ(fenceIdx, modern->num_fences);
+    EXPECT_EQ(NULL, pt);
+}
+
 int SyncFence::s_fenceCount = 0;
 
 TEST(AllocTest, Timeline) {
@@ -225,6 +251,7 @@
 
     SyncFence fence(timeline, 1);
     ASSERT_TRUE(fence.isValid());
+    CheckModernLegacyInfoMatch(fence);
 }
 
 TEST(AllocTest, FenceNegative) {
@@ -321,15 +348,21 @@
     timeline.inc(1);
     ASSERT_EQ(a.getSignaledCount(), 1);
     ASSERT_EQ(d.getActiveCount(), 1);
+    CheckModernLegacyInfoMatch(a);
+    CheckModernLegacyInfoMatch(d);
 
     timeline.inc(1);
     ASSERT_EQ(b.getSignaledCount(), 1);
     ASSERT_EQ(d.getActiveCount(), 1);
+    CheckModernLegacyInfoMatch(b);
+    CheckModernLegacyInfoMatch(d);
 
     timeline.inc(1);
     ASSERT_EQ(c.getSignaledCount(), 1);
     ASSERT_EQ(d.getActiveCount(), 0);
     ASSERT_EQ(d.getSignaledCount(), 1);
+    CheckModernLegacyInfoMatch(c);
+    CheckModernLegacyInfoMatch(d);
 }
 
 TEST(FenceTest, MergeSameFence) {
@@ -343,36 +376,11 @@
     ASSERT_TRUE(selfMergeFence.isValid());
 
     ASSERT_EQ(selfMergeFence.getSignaledCount(), 0);
+    CheckModernLegacyInfoMatch(selfMergeFence);
 
     timeline.inc(5);
     ASSERT_EQ(selfMergeFence.getSignaledCount(), 1);
-}
-
-TEST(FenceTest, WaitOnDestroyedTimeline) {
-    SyncTimeline timeline;
-    ASSERT_TRUE(timeline.isValid());
-
-    SyncFence fenceSig(timeline, 100);
-    SyncFence fenceKill(timeline, 200);
-
-    // Spawn a thread to wait on a fence when the timeline is killed.
-    thread waitThread{
-        [&]() {
-            ASSERT_EQ(timeline.inc(100), 0);
-
-            ASSERT_EQ(fenceKill.wait(-1), -1);
-            ASSERT_EQ(errno, ENOENT);
-        }
-    };
-
-    // Wait for the thread to spool up.
-    fenceSig.wait();
-
-    // Kill the timeline.
-    timeline.destroy();
-
-    // wait for the thread to clean up.
-    waitThread.join();
+    CheckModernLegacyInfoMatch(selfMergeFence);
 }
 
 TEST(FenceTest, PollOnDestroyedTimeline) {
@@ -391,8 +399,7 @@
             struct pollfd fds;
             fds.fd = fenceKill.getFd();
             fds.events = POLLIN | POLLERR;
-            ASSERT_EQ(poll(&fds, 1, -1), 1);
-            ASSERT_TRUE(fds.revents & POLLERR);
+            ASSERT_EQ(poll(&fds, 1, 0), 0);
         }
     };
 
@@ -425,14 +432,17 @@
     timelineA.inc(5);
     ASSERT_EQ(mergedFence.getActiveCount(), 2);
     ASSERT_EQ(mergedFence.getSignaledCount(), 1);
+    CheckModernLegacyInfoMatch(mergedFence);
 
     timelineB.inc(5);
     ASSERT_EQ(mergedFence.getActiveCount(), 1);
     ASSERT_EQ(mergedFence.getSignaledCount(), 2);
+    CheckModernLegacyInfoMatch(mergedFence);
 
     timelineC.inc(5);
     ASSERT_EQ(mergedFence.getActiveCount(), 0);
     ASSERT_EQ(mergedFence.getSignaledCount(), 3);
+    CheckModernLegacyInfoMatch(mergedFence);
 
     // confirm you can successfully wait.
     ASSERT_EQ(mergedFence.wait(100), 0);
@@ -564,7 +574,7 @@
     ASSERT_TRUE(fence.isValid());
 
     unordered_map<int, int> fenceMap;
-    fenceMap.insert(make_tuple(0, 0));
+    fenceMap.insert(make_pair(0, 0));
 
     // Randomly create syncpoints out of a fixed set of timelines, and merge them together.
     for (int i = 0; i < mergeCount; i++) {
@@ -577,17 +587,18 @@
         // Keep track of the latest syncpoint in each timeline.
         auto itr = fenceMap.find(timelineOffset);
         if (itr == end(fenceMap)) {
-            fenceMap.insert(tie(timelineOffset, syncPoint));
+            fenceMap.insert(make_pair(timelineOffset, syncPoint));
         }
         else {
             int oldSyncPoint = itr->second;
             fenceMap.erase(itr);
-            fenceMap.insert(tie(timelineOffset, max(syncPoint, oldSyncPoint)));
+            fenceMap.insert(make_pair(timelineOffset, max(syncPoint, oldSyncPoint)));
         }
 
         // Merge.
         fence = SyncFence(fence, SyncFence(timeline, syncPoint));
         ASSERT_TRUE(fence.isValid());
+        CheckModernLegacyInfoMatch(fence);
     }
 
     // Confirm our map matches the fence.
diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk
index 7bf53e3..584e5a2 100644
--- a/libsysutils/Android.mk
+++ b/libsysutils/Android.mk
@@ -17,9 +17,11 @@
 LOCAL_CFLAGS := -Werror
 
 LOCAL_SHARED_LIBRARIES := \
+        libbase \
         libcutils \
         liblog \
         libnl
 
-include $(BUILD_SHARED_LIBRARY)
+LOCAL_EXPORT_C_INCLUDE_DIRS := system/core/libsysutils/include
 
+include $(BUILD_SHARED_LIBRARY)
diff --git a/include/sysutils/FrameworkClient.h b/libsysutils/include/sysutils/FrameworkClient.h
similarity index 100%
rename from include/sysutils/FrameworkClient.h
rename to libsysutils/include/sysutils/FrameworkClient.h
diff --git a/include/sysutils/FrameworkCommand.h b/libsysutils/include/sysutils/FrameworkCommand.h
similarity index 100%
rename from include/sysutils/FrameworkCommand.h
rename to libsysutils/include/sysutils/FrameworkCommand.h
diff --git a/include/sysutils/FrameworkListener.h b/libsysutils/include/sysutils/FrameworkListener.h
similarity index 100%
rename from include/sysutils/FrameworkListener.h
rename to libsysutils/include/sysutils/FrameworkListener.h
diff --git a/include/sysutils/List.h b/libsysutils/include/sysutils/List.h
similarity index 100%
rename from include/sysutils/List.h
rename to libsysutils/include/sysutils/List.h
diff --git a/include/sysutils/NetlinkEvent.h b/libsysutils/include/sysutils/NetlinkEvent.h
similarity index 100%
rename from include/sysutils/NetlinkEvent.h
rename to libsysutils/include/sysutils/NetlinkEvent.h
diff --git a/include/sysutils/NetlinkListener.h b/libsysutils/include/sysutils/NetlinkListener.h
similarity index 100%
rename from include/sysutils/NetlinkListener.h
rename to libsysutils/include/sysutils/NetlinkListener.h
diff --git a/include/sysutils/ServiceManager.h b/libsysutils/include/sysutils/ServiceManager.h
similarity index 100%
rename from include/sysutils/ServiceManager.h
rename to libsysutils/include/sysutils/ServiceManager.h
diff --git a/include/sysutils/SocketClient.h b/libsysutils/include/sysutils/SocketClient.h
similarity index 100%
rename from include/sysutils/SocketClient.h
rename to libsysutils/include/sysutils/SocketClient.h
diff --git a/include/sysutils/SocketClientCommand.h b/libsysutils/include/sysutils/SocketClientCommand.h
similarity index 100%
rename from include/sysutils/SocketClientCommand.h
rename to libsysutils/include/sysutils/SocketClientCommand.h
diff --git a/include/sysutils/SocketListener.h b/libsysutils/include/sysutils/SocketListener.h
similarity index 100%
rename from include/sysutils/SocketListener.h
rename to libsysutils/include/sysutils/SocketListener.h
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index fef801a..79bc888 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -561,10 +561,12 @@
 static const char*
 has_prefix(const char* str, const char* end, const char* prefix, size_t prefixlen)
 {
-    if ((end-str) >= (ptrdiff_t)prefixlen && !memcmp(str, prefix, prefixlen))
+    if ((end - str) >= (ptrdiff_t)prefixlen &&
+        (prefixlen == 0 || !memcmp(str, prefix, prefixlen))) {
         return str + prefixlen;
-    else
+    } else {
         return NULL;
+    }
 }
 
 /* Same as strlen(x) for constant string literals ONLY */
diff --git a/libsysutils/src/ServiceManager.cpp b/libsysutils/src/ServiceManager.cpp
index 13bac09..c7aa1f7 100644
--- a/libsysutils/src/ServiceManager.cpp
+++ b/libsysutils/src/ServiceManager.cpp
@@ -19,34 +19,23 @@
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
+#include <sys/system_properties.h>
 #include <unistd.h>
 
-#include <cutils/properties.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <log/log.h>
 #include <sysutils/ServiceManager.h>
 
 ServiceManager::ServiceManager() {
 }
 
-/* The service name should not exceed SERVICE_NAME_MAX to avoid
- * some weird things. This is due to the fact that:
- *
- * - Starting a service is done by writing its name to the "ctl.start"
- *   system property. This triggers the init daemon to actually start
- *   the service for us.
- *
- * - Stopping the service is done by writing its name to "ctl.stop"
- *   in a similar way.
- *
- * - Reading the status of a service is done by reading the property
- *   named "init.svc.<name>"
- *
- * If strlen(<name>) > (PROPERTY_KEY_MAX-1)-9, then you can start/stop
- * the service by writing to ctl.start/stop, but you won't be able to
- * read its state due to the truncation of "init.svc.<name>" into a
- * zero-terminated buffer of PROPERTY_KEY_MAX characters.
- */
-#define SERVICE_NAME_MAX  (PROPERTY_KEY_MAX-10)
+// The length of a service name should not exceed SERVICE_NAME_MAX. Starting
+// a service is done by writing its name to the "ctl.start" system property
+// and stopping a service is done by writing its name to "ctl.stop". If a
+// service name is too long to fit in a property, you won't be able to start
+// or stop it.
+static constexpr size_t SERVICE_NAME_MAX = PROP_VALUE_MAX;
 
 /* The maximum amount of time to wait for a service to start or stop,
  * in micro-seconds (really an approximation) */
@@ -61,13 +50,14 @@
         SLOGE("Service name '%s' is too long", name);
         return 0;
     }
+
     if (isRunning(name)) {
         SLOGW("Service '%s' is already running", name);
         return 0;
     }
 
     SLOGD("Starting service '%s'", name);
-    property_set("ctl.start", name);
+    android::base::SetProperty("ctl.start", name);
 
     int count = SLEEP_MAX_USEC;
     while(count > 0) {
@@ -90,13 +80,14 @@
         SLOGE("Service name '%s' is too long", name);
         return 0;
     }
+
     if (!isRunning(name)) {
         SLOGW("Service '%s' is already stopped", name);
         return 0;
     }
 
     SLOGD("Stopping service '%s'", name);
-    property_set("ctl.stop", name);
+    android::base::SetProperty("ctl.stop", name);
 
     int count = SLEEP_MAX_USEC;
     while(count > 0) {
@@ -116,19 +107,6 @@
 }
 
 bool ServiceManager::isRunning(const char *name) {
-    char propVal[PROPERTY_VALUE_MAX];
-    char propName[PROPERTY_KEY_MAX];
-    int  ret;
-
-    ret = snprintf(propName, sizeof(propName), "init.svc.%s", name);
-    if (ret > (int)sizeof(propName)-1) {
-        SLOGD("Service name '%s' is too long", name);
-        return false;
-    }
-
-    if (property_get(propName, propVal, NULL)) {
-        if (!strcmp(propVal, "running"))
-            return true;
-    }
-    return false;
+    std::string property_name = android::base::StringPrintf("init.svc.%s", name);
+    return (android::base::GetProperty(property_name, "") == "running");
 }
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
new file mode 100644
index 0000000..9bb1304
--- /dev/null
+++ b/libunwindstack/Android.bp
@@ -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.
+//
+
+cc_defaults {
+    name: "libunwindstack_flags",
+
+    host_supported: true,
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+}
+
+cc_defaults {
+    name: "libunwindstack_common",
+    defaults: ["libunwindstack_flags"],
+
+    srcs: [
+        "ArmExidx.cpp",
+        "Memory.cpp",
+        "Log.cpp",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+}
+
+cc_library {
+    name: "libunwindstack",
+    defaults: ["libunwindstack_common"],
+}
+
+cc_library {
+    name: "libunwindstack_debug",
+    defaults: ["libunwindstack_common"],
+
+    cflags: [
+        "-UNDEBUG",
+        "-O0",
+        "-g",
+    ],
+}
+
+//-------------------------------------------------------------------------
+// Unit Tests
+//-------------------------------------------------------------------------
+cc_defaults {
+    name: "libunwindstack_test_common",
+    defaults: ["libunwindstack_flags"],
+
+    srcs: [
+        "tests/ArmExidxDecodeTest.cpp",
+        "tests/ArmExidxExtractTest.cpp",
+        "tests/LogFake.cpp",
+        "tests/MemoryFake.cpp",
+        "tests/MemoryFileTest.cpp",
+        "tests/MemoryLocalTest.cpp",
+        "tests/MemoryRangeTest.cpp",
+        "tests/MemoryRemoteTest.cpp",
+        "tests/RegsTest.cpp",
+    ],
+
+    cflags: [
+        "-O0",
+        "-g",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+
+    target: {
+        linux: {
+            host_ldlibs: [
+                "-lrt",
+            ],
+        },
+    },
+}
+
+// These unit tests run against the shared library.
+cc_test {
+    name: "libunwindstack_test",
+    defaults: ["libunwindstack_test_common"],
+
+    shared_libs: [
+        "libunwindstack",
+    ],
+}
+
+// These unit tests run against the static debug library.
+cc_test {
+    name: "libunwindstack_test_debug",
+    defaults: ["libunwindstack_test_common"],
+
+    static_libs: [
+        "libunwindstack_debug",
+    ],
+}
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
new file mode 100644
index 0000000..3b78918
--- /dev/null
+++ b/libunwindstack/ArmExidx.cpp
@@ -0,0 +1,680 @@
+/*
+ * 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 <assert.h>
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+
+#include <android-base/stringprintf.h>
+
+#include "ArmExidx.h"
+#include "Log.h"
+#include "Machine.h"
+
+void ArmExidx::LogRawData() {
+  std::string log_str("Raw Data:");
+  for (const uint8_t data : data_) {
+    log_str += android::base::StringPrintf(" 0x%02x", data);
+  }
+  log(log_indent_, log_str.c_str());
+}
+
+bool ArmExidx::ExtractEntryData(uint32_t entry_offset) {
+  data_.clear();
+  status_ = ARM_STATUS_NONE;
+
+  if (entry_offset & 1) {
+    // The offset needs to be at least two byte aligned.
+    status_ = ARM_STATUS_INVALID_ALIGNMENT;
+    return false;
+  }
+
+  // Each entry is a 32 bit prel31 offset followed by 32 bits
+  // of unwind information. If bit 31 of the unwind data is zero,
+  // then this is a prel31 offset to the start of the unwind data.
+  // If the unwind data is 1, then this is a cant unwind entry.
+  // Otherwise, this data is the compact form of the unwind information.
+  uint32_t data;
+  if (!elf_memory_->Read32(entry_offset + 4, &data)) {
+    status_ = ARM_STATUS_READ_FAILED;
+    return false;
+  }
+  if (data == 1) {
+    // This is a CANT UNWIND entry.
+    status_ = ARM_STATUS_NO_UNWIND;
+    if (log_) {
+      log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
+      log(log_indent_, "[cantunwind]");
+    }
+    return false;
+  }
+
+  if (data & (1UL << 31)) {
+    // This is a compact table entry.
+    if ((data >> 24) & 0xf) {
+      // This is a non-zero index, this code doesn't support
+      // other formats.
+      status_ = ARM_STATUS_INVALID_PERSONALITY;
+      return false;
+    }
+    data_.push_back((data >> 16) & 0xff);
+    data_.push_back((data >> 8) & 0xff);
+    uint8_t last_op = data & 0xff;
+    data_.push_back(last_op);
+    if (last_op != ARM_OP_FINISH) {
+      // If this didn't end with a finish op, add one.
+      data_.push_back(ARM_OP_FINISH);
+    }
+    if (log_) {
+      LogRawData();
+    }
+    return true;
+  }
+
+  // Get the address of the ops.
+  // Sign extend the data value if necessary.
+  int32_t signed_data = static_cast<int32_t>(data << 1) >> 1;
+  uint32_t addr = (entry_offset + 4) + signed_data;
+  if (!elf_memory_->Read32(addr, &data)) {
+    status_ = ARM_STATUS_READ_FAILED;
+    return false;
+  }
+
+  size_t num_table_words;
+  if (data & (1UL << 31)) {
+    // Compact model.
+    switch ((data >> 24) & 0xf) {
+    case 0:
+      num_table_words = 0;
+      data_.push_back((data >> 16) & 0xff);
+      break;
+    case 1:
+    case 2:
+      num_table_words = (data >> 16) & 0xff;
+      addr += 4;
+      break;
+    default:
+      // Only a personality of 0, 1, 2 is valid.
+      status_ = ARM_STATUS_INVALID_PERSONALITY;
+      return false;
+    }
+    data_.push_back((data >> 8) & 0xff);
+    data_.push_back(data & 0xff);
+  } else {
+    // Generic model.
+
+    // Skip the personality routine data, it doesn't contain any data
+    // needed to decode the unwind information.
+    addr += 4;
+    if (!elf_memory_->Read32(addr, &data)) {
+      status_ = ARM_STATUS_READ_FAILED;
+      return false;
+    }
+    num_table_words = (data >> 24) & 0xff;
+    data_.push_back((data >> 16) & 0xff);
+    data_.push_back((data >> 8) & 0xff);
+    data_.push_back(data & 0xff);
+    addr += 4;
+  }
+
+  if (num_table_words > 5) {
+    status_ = ARM_STATUS_MALFORMED;
+    return false;
+  }
+
+  for (size_t i = 0; i < num_table_words; i++) {
+    if (!elf_memory_->Read32(addr, &data)) {
+      status_ = ARM_STATUS_READ_FAILED;
+      return false;
+    }
+    data_.push_back((data >> 24) & 0xff);
+    data_.push_back((data >> 16) & 0xff);
+    data_.push_back((data >> 8) & 0xff);
+    data_.push_back(data & 0xff);
+    addr += 4;
+  }
+
+  if (data_.back() != ARM_OP_FINISH) {
+    // If this didn't end with a finish op, add one.
+    data_.push_back(ARM_OP_FINISH);
+  }
+
+  if (log_) {
+    LogRawData();
+  }
+  return true;
+}
+
+inline bool ArmExidx::GetByte(uint8_t* byte) {
+  if (data_.empty()) {
+    status_ = ARM_STATUS_TRUNCATED;
+    return false;
+  }
+  *byte = data_.front();
+  data_.pop_front();
+  return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_00(uint8_t byte) {
+  assert((byte >> 4) == 0x8);
+
+  uint16_t registers = (byte & 0xf) << 8;
+  if (!GetByte(&byte)) {
+    return false;
+  }
+
+  registers |= byte;
+  if (registers == 0) {
+    // 10000000 00000000: Refuse to unwind
+    if (log_) {
+      log(log_indent_, "Refuse to unwind");
+    }
+    status_ = ARM_STATUS_NO_UNWIND;
+    return false;
+  }
+  // 1000iiii iiiiiiii: Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}
+  if (log_) {
+    bool add_comma = false;
+    std::string msg = "pop {";
+    for (size_t i = 0; i < 12; i++) {
+      if (registers & (1 << i)) {
+        if (add_comma) {
+          msg += ", ";
+        }
+        msg += android::base::StringPrintf("r%zu", i + 4);
+        add_comma = true;
+      }
+    }
+    log(log_indent_, "%s}", msg.c_str());
+    if (log_skip_execution_) {
+      return true;
+    }
+  }
+
+  registers <<= 4;
+  for (size_t reg = 4; reg < 16; reg++) {
+    if (registers & (1 << reg)) {
+      if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
+        status_ = ARM_STATUS_READ_FAILED;
+        return false;
+      }
+      cfa_ += 4;
+    }
+  }
+  // If the sp register is modified, change the cfa value.
+  if (registers & (1 << ARM_REG_SP)) {
+    cfa_ = (*regs_)[ARM_REG_SP];
+  }
+  return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_01(uint8_t byte) {
+  assert((byte >> 4) == 0x9);
+
+  uint8_t bits = byte & 0xf;
+  if (bits == 13 || bits == 15) {
+    // 10011101: Reserved as prefix for ARM register to register moves
+    // 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
+    if (log_) {
+      log(log_indent_, "[Reserved]");
+    }
+    status_ = ARM_STATUS_RESERVED;
+    return false;
+  }
+  // 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
+  if (log_) {
+    log(log_indent_, "vsp = r%d", bits);
+    if (log_skip_execution_) {
+      return true;
+    }
+  }
+  // It is impossible for bits to be larger than the total number of
+  // arm registers, so don't bother checking if bits is a valid register.
+  cfa_ = (*regs_)[bits];
+  return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_10(uint8_t byte) {
+  assert((byte >> 4) == 0xa);
+
+  // 10100nnn: Pop r4-r[4+nnn]
+  // 10101nnn: Pop r4-r[4+nnn], r14
+  if (log_) {
+    std::string msg = "pop {r4";
+    uint8_t end_reg = byte & 0x7;
+    if (end_reg) {
+      msg += android::base::StringPrintf("-r%d", 4 + end_reg);
+    }
+    if (byte & 0x8) {
+      log(log_indent_, "%s, r14}", msg.c_str());
+    } else {
+      log(log_indent_, "%s}", msg.c_str());
+    }
+    if (log_skip_execution_) {
+      return true;
+    }
+  }
+
+  for (size_t i = 4; i <= 4 + (byte & 0x7); i++) {
+    if (!process_memory_->Read32(cfa_, &(*regs_)[i])) {
+      status_ = ARM_STATUS_READ_FAILED;
+      return false;
+    }
+    cfa_ += 4;
+  }
+  if (byte & 0x8) {
+    if (!process_memory_->Read32(cfa_, &(*regs_)[ARM_REG_R14])) {
+      status_ = ARM_STATUS_READ_FAILED;
+      return false;
+    }
+    cfa_ += 4;
+  }
+  return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_0000() {
+  // 10110000: Finish
+  if (log_) {
+    log(log_indent_, "finish");
+    if (log_skip_execution_) {
+      status_ = ARM_STATUS_FINISH;
+      return false;
+    }
+  }
+  if (!(*regs_)[ARM_REG_PC]) {
+    (*regs_)[ARM_REG_PC] = (*regs_)[ARM_REG_LR];
+  }
+  status_ = ARM_STATUS_FINISH;
+  return false;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_0001() {
+  uint8_t byte;
+  if (!GetByte(&byte)) {
+    return false;
+  }
+
+  if (byte == 0) {
+    // 10110001 00000000: Spare
+    if (log_) {
+      log(log_indent_, "Spare");
+    }
+    status_ = ARM_STATUS_SPARE;
+    return false;
+  }
+  if (byte >> 4) {
+    // 10110001 xxxxyyyy: Spare (xxxx != 0000)
+    if (log_) {
+      log(log_indent_, "Spare");
+    }
+    status_ = ARM_STATUS_SPARE;
+    return false;
+  }
+
+  // 10110001 0000iiii: Pop integer registers under mask {r3, r2, r1, r0}
+  if (log_) {
+    bool add_comma = false;
+    std::string msg = "pop {";
+    for (size_t i = 0; i < 4; i++) {
+      if (byte & (1 << i)) {
+        if (add_comma) {
+          msg += ", ";
+        }
+        msg += android::base::StringPrintf("r%zu", i);
+        add_comma = true;
+      }
+    }
+    log(log_indent_, "%s}", msg.c_str());
+    if (log_skip_execution_) {
+      return true;
+    }
+  }
+
+  for (size_t reg = 0; reg < 4; reg++) {
+    if (byte & (1 << reg)) {
+      if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
+        status_ = ARM_STATUS_READ_FAILED;
+        return false;
+      }
+      cfa_ += 4;
+    }
+  }
+  return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_0010() {
+  // 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
+  uint32_t result = 0;
+  uint32_t shift = 0;
+  uint8_t byte;
+  do {
+    if (!GetByte(&byte)) {
+      return false;
+    }
+
+    result |= (byte & 0x7f) << shift;
+    shift += 7;
+  } while (byte & 0x80);
+  result <<= 2;
+  if (log_) {
+    log(log_indent_, "vsp = vsp + %d", 0x204 + result);
+    if (log_skip_execution_) {
+      return true;
+    }
+  }
+  cfa_ += 0x204 + result;
+  return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_0011() {
+  // 10110011 sssscccc: Pop VFP double precision registers D[ssss]-D[ssss+cccc] by FSTMFDX
+  uint8_t byte;
+  if (!GetByte(&byte)) {
+    return false;
+  }
+
+  if (log_) {
+    uint8_t start_reg = byte >> 4;
+    std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
+    uint8_t end_reg = start_reg + (byte & 0xf);
+    if (end_reg) {
+      msg += android::base::StringPrintf("-d%d", end_reg);
+    }
+    log(log_indent_, "%s}", msg.c_str());
+    if (log_skip_execution_) {
+      return true;
+    }
+  }
+  cfa_ += (byte & 0xf) * 8 + 12;
+  return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_01nn() {
+  // 101101nn: Spare
+  if (log_) {
+    log(log_indent_, "Spare");
+  }
+  status_ = ARM_STATUS_SPARE;
+  return false;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_1nnn(uint8_t byte) {
+  assert((byte & ~0x07) == 0xb8);
+
+  // 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX
+  if (log_) {
+    std::string msg = "pop {d8";
+    uint8_t last_reg = (byte & 0x7);
+    if (last_reg) {
+      msg += android::base::StringPrintf("-d%d", last_reg + 8);
+    }
+    log(log_indent_, "%s}", msg.c_str());
+    if (log_skip_execution_) {
+      return true;
+    }
+  }
+  // Only update the cfa.
+  cfa_ += (byte & 0x7) * 8 + 12;
+  return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10(uint8_t byte) {
+  assert((byte >> 6) == 0x2);
+
+  switch ((byte >> 4) & 0x3) {
+  case 0:
+    return DecodePrefix_10_00(byte);
+  case 1:
+    return DecodePrefix_10_01(byte);
+  case 2:
+    return DecodePrefix_10_10(byte);
+  default:
+    switch (byte & 0xf) {
+    case 0:
+      return DecodePrefix_10_11_0000();
+    case 1:
+      return DecodePrefix_10_11_0001();
+    case 2:
+      return DecodePrefix_10_11_0010();
+    case 3:
+      return DecodePrefix_10_11_0011();
+    default:
+      if (byte & 0x8) {
+        return DecodePrefix_10_11_1nnn(byte);
+      } else {
+        return DecodePrefix_10_11_01nn();
+      }
+    }
+  }
+}
+
+inline bool ArmExidx::DecodePrefix_11_000(uint8_t byte) {
+  assert((byte & ~0x07) == 0xc0);
+
+  uint8_t bits = byte & 0x7;
+  if (bits == 6) {
+    if (!GetByte(&byte)) {
+      return false;
+    }
+
+    // 11000110 sssscccc: Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]
+    if (log_) {
+      uint8_t start_reg = byte >> 4;
+      std::string msg = android::base::StringPrintf("pop {wR%d", start_reg);
+      uint8_t end_reg = byte & 0xf;
+      if (end_reg) {
+        msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
+      }
+      log(log_indent_, "%s}", msg.c_str());
+      if (log_skip_execution_) {
+        return true;
+      }
+    }
+    // Only update the cfa.
+    cfa_ += (byte & 0xf) * 8 + 8;
+  } else if (bits == 7) {
+    if (!GetByte(&byte)) {
+      return false;
+    }
+
+    if (byte == 0) {
+      // 11000111 00000000: Spare
+      if (log_) {
+        log(log_indent_, "Spare");
+      }
+      status_ = ARM_STATUS_SPARE;
+      return false;
+    } else if ((byte >> 4) == 0) {
+      // 11000111 0000iiii: Intel Wireless MMX pop wCGR registers {wCGR0,1,2,3}
+      if (log_) {
+        bool add_comma = false;
+        std::string msg = "pop {";
+        for (size_t i = 0; i < 4; i++) {
+          if (byte & (1 << i)) {
+            if (add_comma) {
+              msg += ", ";
+            }
+            msg += android::base::StringPrintf("wCGR%zu", i);
+            add_comma = true;
+          }
+        }
+        log(log_indent_, "%s}", msg.c_str());
+      }
+      // Only update the cfa.
+      cfa_ += __builtin_popcount(byte) * 4;
+    } else {
+      // 11000111 xxxxyyyy: Spare (xxxx != 0000)
+      if (log_) {
+        log(log_indent_, "Spare");
+      }
+      status_ = ARM_STATUS_SPARE;
+      return false;
+    }
+  } else {
+    // 11000nnn: Intel Wireless MMX pop wR[10]-wR[10+nnn] (nnn != 6, 7)
+    if (log_) {
+      std::string msg = "pop {wR10";
+      uint8_t nnn = byte & 0x7;
+      if (nnn) {
+        msg += android::base::StringPrintf("-wR%d", 10 + nnn);
+      }
+      log(log_indent_, "%s}", msg.c_str());
+      if (log_skip_execution_) {
+        return true;
+      }
+    }
+    // Only update the cfa.
+    cfa_ += (byte & 0x7) * 8 + 8;
+  }
+  return true;
+}
+
+inline bool ArmExidx::DecodePrefix_11_001(uint8_t byte) {
+  assert((byte & ~0x07) == 0xc8);
+
+  uint8_t bits = byte & 0x7;
+  if (bits == 0) {
+    // 11001000 sssscccc: Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc] by VPUSH
+    if (!GetByte(&byte)) {
+      return false;
+    }
+
+    if (log_) {
+      uint8_t start_reg = byte >> 4;
+      std::string msg = android::base::StringPrintf("pop {d%d", 16 + start_reg);
+      uint8_t end_reg = byte & 0xf;
+      if (end_reg) {
+        msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
+      }
+      log(log_indent_, "%s}", msg.c_str());
+      if (log_skip_execution_) {
+        return true;
+      }
+    }
+    // Only update the cfa.
+    cfa_ += (byte & 0xf) * 8 + 8;
+  } else if (bits == 1) {
+    // 11001001 sssscccc: Pop VFP double precision registers D[ssss]-D[ssss+cccc] by VPUSH
+    if (!GetByte(&byte)) {
+      return false;
+    }
+
+    if (log_) {
+      uint8_t start_reg = byte >> 4;
+      std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
+      uint8_t end_reg = byte & 0xf;
+      if (end_reg) {
+        msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
+      }
+      log(log_indent_, "%s}", msg.c_str());
+      if (log_skip_execution_) {
+        return true;
+      }
+    }
+    // Only update the cfa.
+    cfa_ += (byte & 0xf) * 8 + 8;
+  } else {
+    // 11001yyy: Spare (yyy != 000, 001)
+    if (log_) {
+      log(log_indent_, "Spare");
+    }
+    status_ = ARM_STATUS_SPARE;
+    return false;
+  }
+  return true;
+}
+
+inline bool ArmExidx::DecodePrefix_11_010(uint8_t byte) {
+  assert((byte & ~0x07) == 0xd0);
+
+  // 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
+  if (log_) {
+    std::string msg = "pop {d8";
+    uint8_t end_reg = byte & 0x7;
+    if (end_reg) {
+      msg += android::base::StringPrintf("-d%d", 8 + end_reg);
+    }
+    log(log_indent_, "%s}", msg.c_str());
+    if (log_skip_execution_) {
+      return true;
+    }
+  }
+  cfa_ += (byte & 0x7) * 8 + 8;
+  return true;
+}
+
+inline bool ArmExidx::DecodePrefix_11(uint8_t byte) {
+  assert((byte >> 6) == 0x3);
+
+  switch ((byte >> 3) & 0x7) {
+  case 0:
+    return DecodePrefix_11_000(byte);
+  case 1:
+    return DecodePrefix_11_001(byte);
+  case 2:
+    return DecodePrefix_11_010(byte);
+  default:
+    // 11xxxyyy: Spare (xxx != 000, 001, 010)
+    if (log_) {
+      log(log_indent_, "Spare");
+    }
+    status_ = ARM_STATUS_SPARE;
+    return false;
+  }
+}
+
+bool ArmExidx::Decode() {
+  status_ = ARM_STATUS_NONE;
+  uint8_t byte;
+  if (!GetByte(&byte)) {
+    return false;
+  }
+
+  switch (byte >> 6) {
+  case 0:
+    // 00xxxxxx: vsp = vsp + (xxxxxxx << 2) + 4
+    if (log_) {
+      log(log_indent_, "vsp = vsp + %d", ((byte & 0x3f) << 2) + 4);
+      if (log_skip_execution_) {
+        break;
+      }
+    }
+    cfa_ += ((byte & 0x3f) << 2) + 4;
+    break;
+  case 1:
+    // 01xxxxxx: vsp = vsp - (xxxxxxx << 2) + 4
+    if (log_) {
+      log(log_indent_, "vsp = vsp - %d", ((byte & 0x3f) << 2) + 4);
+      if (log_skip_execution_) {
+        break;
+      }
+    }
+    cfa_ -= ((byte & 0x3f) << 2) + 4;
+    break;
+  case 2:
+    return DecodePrefix_10(byte);
+  default:
+    return DecodePrefix_11(byte);
+  }
+  return true;
+}
+
+bool ArmExidx::Eval() {
+  while (Decode());
+  return status_ == ARM_STATUS_FINISH;
+}
diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h
new file mode 100644
index 0000000..a92caef
--- /dev/null
+++ b/libunwindstack/ArmExidx.h
@@ -0,0 +1,103 @@
+/*
+ * 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_ARM_EXIDX_H
+#define _LIBUNWINDSTACK_ARM_EXIDX_H
+
+#include <stdint.h>
+
+#include <deque>
+
+#include "Memory.h"
+#include "Regs.h"
+
+enum ArmStatus : size_t {
+  ARM_STATUS_NONE = 0,
+  ARM_STATUS_NO_UNWIND,
+  ARM_STATUS_FINISH,
+  ARM_STATUS_RESERVED,
+  ARM_STATUS_SPARE,
+  ARM_STATUS_TRUNCATED,
+  ARM_STATUS_READ_FAILED,
+  ARM_STATUS_MALFORMED,
+  ARM_STATUS_INVALID_ALIGNMENT,
+  ARM_STATUS_INVALID_PERSONALITY,
+};
+
+enum ArmOp : uint8_t {
+  ARM_OP_FINISH = 0xb0,
+};
+
+class ArmExidx {
+ public:
+  ArmExidx(Regs32* regs, Memory* elf_memory, Memory* process_memory)
+      : regs_(regs), elf_memory_(elf_memory), process_memory_(process_memory) {}
+  virtual ~ArmExidx() {}
+
+  void LogRawData();
+
+  bool ExtractEntryData(uint32_t entry_offset);
+
+  bool Eval();
+
+  bool Decode();
+
+  std::deque<uint8_t>* data() { return &data_; }
+
+  ArmStatus status() { return status_; }
+
+  Regs32* regs() { return regs_; }
+
+  uint32_t cfa() { return cfa_; }
+  void set_cfa(uint32_t cfa) { cfa_ = cfa; }
+
+  void set_log(bool log) { log_ = log; }
+  void set_log_skip_execution(bool skip_execution) { log_skip_execution_ = skip_execution; }
+  void set_log_indent(uint8_t indent) { log_indent_ = indent; }
+
+ private:
+  bool GetByte(uint8_t* byte);
+
+  bool DecodePrefix_10_00(uint8_t byte);
+  bool DecodePrefix_10_01(uint8_t byte);
+  bool DecodePrefix_10_10(uint8_t byte);
+  bool DecodePrefix_10_11_0000();
+  bool DecodePrefix_10_11_0001();
+  bool DecodePrefix_10_11_0010();
+  bool DecodePrefix_10_11_0011();
+  bool DecodePrefix_10_11_01nn();
+  bool DecodePrefix_10_11_1nnn(uint8_t byte);
+  bool DecodePrefix_10(uint8_t byte);
+
+  bool DecodePrefix_11_000(uint8_t byte);
+  bool DecodePrefix_11_001(uint8_t byte);
+  bool DecodePrefix_11_010(uint8_t byte);
+  bool DecodePrefix_11(uint8_t byte);
+
+  Regs32* regs_ = nullptr;
+  uint32_t cfa_ = 0;
+  std::deque<uint8_t> data_;
+  ArmStatus status_ = ARM_STATUS_NONE;
+
+  Memory* elf_memory_;
+  Memory* process_memory_;
+
+  bool log_ = false;
+  uint8_t log_indent_ = 0;
+  bool log_skip_execution_ = false;
+};
+
+#endif  // _LIBUNWINDSTACK_ARM_EXIDX_H
diff --git a/libunwindstack/Log.cpp b/libunwindstack/Log.cpp
new file mode 100644
index 0000000..faeb66c
--- /dev/null
+++ b/libunwindstack/Log.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <string>
+
+#define LOG_TAG "unwind"
+#include <log/log.h>
+
+#include <android-base/stringprintf.h>
+
+#include "Log.h"
+
+static bool g_print_to_stdout = false;
+
+void log_to_stdout(bool enable) {
+  g_print_to_stdout = enable;
+}
+
+// Send the data to the log.
+void log(uint8_t indent, const char* format, ...) {
+  std::string real_format;
+  if (indent > 0) {
+    real_format = android::base::StringPrintf("%*s%s", 2 * indent, " ", format);
+  } else {
+    real_format = format;
+  }
+  va_list args;
+  va_start(args, format);
+  if (g_print_to_stdout) {
+    real_format += '\n';
+    vprintf(real_format.c_str(), args);
+  } else {
+    LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, real_format.c_str(), args);
+  }
+  va_end(args);
+}
diff --git a/bootstat/histogram_logger.h b/libunwindstack/Log.h
similarity index 70%
copy from bootstat/histogram_logger.h
copy to libunwindstack/Log.h
index 60c7776..2d01aa8 100644
--- a/bootstat/histogram_logger.h
+++ b/libunwindstack/Log.h
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-#include <cstdint>
-#include <string>
+#ifndef _LIBUNWINDSTACK_LOG_H
+#define _LIBUNWINDSTACK_LOG_H
 
-namespace bootstat {
+#include <stdint.h>
 
-// Builds an EventLog buffer named |event| containing |data| and writes
-// the log into the Tron histogram logs.
-void LogHistogram(const std::string& event, int32_t data);
+void log_to_stdout(bool enable);
+void log(uint8_t indent, const char* format, ...);
 
-}  // namespace bootstat
\ No newline at end of file
+#endif  // _LIBUNWINDSTACK_LOG_H
diff --git a/libunwindstack/Machine.h b/libunwindstack/Machine.h
new file mode 100644
index 0000000..db84271
--- /dev/null
+++ b/libunwindstack/Machine.h
@@ -0,0 +1,135 @@
+/*
+ * 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_MACHINE_H
+#define _LIBUNWINDSTACK_MACHINE_H
+
+#include <stdint.h>
+
+class Regs;
+
+enum ArmReg : uint16_t {
+  ARM_REG_R0 = 0,
+  ARM_REG_R1,
+  ARM_REG_R2,
+  ARM_REG_R3,
+  ARM_REG_R4,
+  ARM_REG_R5,
+  ARM_REG_R6,
+  ARM_REG_R7,
+  ARM_REG_R8,
+  ARM_REG_R9,
+  ARM_REG_R10,
+  ARM_REG_R11,
+  ARM_REG_R12,
+  ARM_REG_R13,
+  ARM_REG_R14,
+  ARM_REG_R15,
+  ARM_REG_LAST,
+
+  ARM_REG_SP = ARM_REG_R13,
+  ARM_REG_LR = ARM_REG_R14,
+  ARM_REG_PC = ARM_REG_R15,
+};
+
+enum Arm64Reg : uint16_t {
+  ARM64_REG_R0 = 0,
+  ARM64_REG_R1,
+  ARM64_REG_R2,
+  ARM64_REG_R3,
+  ARM64_REG_R4,
+  ARM64_REG_R5,
+  ARM64_REG_R6,
+  ARM64_REG_R7,
+  ARM64_REG_R8,
+  ARM64_REG_R9,
+  ARM64_REG_R10,
+  ARM64_REG_R11,
+  ARM64_REG_R12,
+  ARM64_REG_R13,
+  ARM64_REG_R14,
+  ARM64_REG_R15,
+  ARM64_REG_R16,
+  ARM64_REG_R17,
+  ARM64_REG_R18,
+  ARM64_REG_R19,
+  ARM64_REG_R20,
+  ARM64_REG_R21,
+  ARM64_REG_R22,
+  ARM64_REG_R23,
+  ARM64_REG_R24,
+  ARM64_REG_R25,
+  ARM64_REG_R26,
+  ARM64_REG_R27,
+  ARM64_REG_R28,
+  ARM64_REG_R29,
+  ARM64_REG_R30,
+  ARM64_REG_R31,
+  ARM64_REG_PC,
+  ARM64_REG_LAST,
+
+  ARM64_REG_SP = ARM64_REG_R31,
+  ARM64_REG_LR = ARM64_REG_R30,
+};
+
+enum X86Reg : uint16_t {
+  X86_REG_EAX = 0,
+  X86_REG_ECX,
+  X86_REG_EDX,
+  X86_REG_EBX,
+  X86_REG_ESP,
+  X86_REG_EBP,
+  X86_REG_ESI,
+  X86_REG_EDI,
+  X86_REG_EIP,
+  X86_REG_EFL,
+  X86_REG_CS,
+  X86_REG_SS,
+  X86_REG_DS,
+  X86_REG_ES,
+  X86_REG_FS,
+  X86_REG_GS,
+  X86_REG_LAST,
+
+  X86_REG_SP = X86_REG_ESP,
+  X86_REG_PC = X86_REG_EIP,
+};
+
+enum X86_64Reg : uint16_t {
+  X86_64_REG_RAX = 0,
+  X86_64_REG_RDX,
+  X86_64_REG_RCX,
+  X86_64_REG_RBX,
+  X86_64_REG_RSI,
+  X86_64_REG_RDI,
+  X86_64_REG_RBP,
+  X86_64_REG_RSP,
+  X86_64_REG_R8,
+  X86_64_REG_R9,
+  X86_64_REG_R10,
+  X86_64_REG_R11,
+  X86_64_REG_R12,
+  X86_64_REG_R13,
+  X86_64_REG_R14,
+  X86_64_REG_R15,
+  X86_64_REG_RIP,
+  X86_64_REG_LAST,
+
+  X86_64_REG_SP = X86_64_REG_RSP,
+  X86_64_REG_PC = X86_64_REG_RIP,
+};
+
+#endif  // _LIBUNWINDSTACK_MACHINE_H
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
new file mode 100644
index 0000000..336e4fe
--- /dev/null
+++ b/libunwindstack/Memory.cpp
@@ -0,0 +1,180 @@
+/*
+ * 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 <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+
+#include <android-base/unique_fd.h>
+
+#include "Memory.h"
+
+bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
+  string->clear();
+  uint64_t bytes_read = 0;
+  while (bytes_read < max_read) {
+    uint8_t value;
+    if (!Read(addr, &value, sizeof(value))) {
+      return false;
+    }
+    if (value == '\0') {
+      return true;
+    }
+    string->push_back(value);
+    addr++;
+    bytes_read++;
+  }
+  return false;
+}
+
+MemoryFileAtOffset::~MemoryFileAtOffset() {
+  if (data_) {
+    munmap(&data_[-offset_], size_ + offset_);
+    data_ = nullptr;
+  }
+}
+
+bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset) {
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
+  if (fd == -1) {
+    return false;
+  }
+  struct stat buf;
+  if (fstat(fd, &buf) == -1) {
+    return false;
+  }
+  if (offset >= static_cast<uint64_t>(buf.st_size)) {
+    return false;
+  }
+
+  offset_ = offset & (getpagesize() - 1);
+  uint64_t aligned_offset = offset & ~(getpagesize() - 1);
+  size_ = buf.st_size - aligned_offset;
+  void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset);
+  if (map == MAP_FAILED) {
+    return false;
+  }
+
+  data_ = &reinterpret_cast<uint8_t*>(map)[offset_];
+  size_ -= offset_;
+
+  return true;
+}
+
+bool MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) {
+  if (addr + size > size_) {
+    return false;
+  }
+  memcpy(dst, &data_[addr], size);
+  return true;
+}
+
+static bool PtraceRead(pid_t pid, uint64_t addr, long* value) {
+  // 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);
+  if (*value == -1 && errno) {
+    return false;
+  }
+  return true;
+}
+
+bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) {
+  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)) {
+      return false;
+    }
+    size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes);
+    memcpy(dst, reinterpret_cast<uint8_t*>(&data) + align_bytes, copy_bytes);
+    addr += copy_bytes;
+    dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + copy_bytes);
+    bytes -= copy_bytes;
+    bytes_read += copy_bytes;
+  }
+
+  for (size_t i = 0; i < bytes / sizeof(long); i++) {
+    if (!PtraceRead(pid_, addr, &data)) {
+      return false;
+    }
+    memcpy(dst, &data, sizeof(long));
+    dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + sizeof(long));
+    addr += sizeof(long);
+    bytes_read += sizeof(long);
+  }
+
+  size_t left_over = bytes & (sizeof(long) - 1);
+  if (left_over) {
+    if (!PtraceRead(pid_, addr, &data)) {
+      return false;
+    }
+    memcpy(dst, &data, left_over);
+    bytes_read += left_over;
+  }
+  return true;
+}
+
+bool MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
+  // The process_vm_readv call does 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.
+  struct iovec local_io;
+  local_io.iov_base = dst;
+  local_io.iov_len = size;
+
+  struct iovec remote_io;
+  remote_io.iov_base = reinterpret_cast<void*>(static_cast<uintptr_t>(addr));
+  remote_io.iov_len = size;
+
+  ssize_t bytes_read = process_vm_readv(getpid(), &local_io, 1, &remote_io, 1, 0);
+  if (bytes_read == -1) {
+    return false;
+  }
+  return static_cast<size_t>(bytes_read) == size;
+}
+
+bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
+  if (!MemoryFileAtOffset::Init(file, offset)) {
+    return false;
+  }
+  // The first uint64_t value is the start of memory.
+  if (!MemoryFileAtOffset::Read(0, &start_, sizeof(start_))) {
+    return false;
+  }
+  // Subtract the first 64 bit value from the total size.
+  size_ -= sizeof(start_);
+  return true;
+}
+
+bool MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
+  if (addr < start_ || addr + size > start_ + offset_ + size_) {
+    return false;
+  }
+  memcpy(dst, &data_[addr + offset_ - start_ + sizeof(start_)], size);
+  return true;
+}
diff --git a/libunwindstack/Memory.h b/libunwindstack/Memory.h
new file mode 100644
index 0000000..5ab031d
--- /dev/null
+++ b/libunwindstack/Memory.h
@@ -0,0 +1,121 @@
+/*
+ * 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_MEMORY_H
+#define _LIBUNWINDSTACK_MEMORY_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+constexpr bool kMemoryStatsEnabled = true;
+
+class Memory {
+ public:
+  Memory() = default;
+  virtual ~Memory() = default;
+
+  virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
+
+  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 Read32(uint64_t addr, uint32_t* dst) {
+    return Read(addr, dst, sizeof(uint32_t));
+  }
+
+  inline bool Read64(uint64_t addr, uint64_t* dst) {
+    return Read(addr, dst, sizeof(uint64_t));
+  }
+};
+
+class MemoryFileAtOffset : public Memory {
+ public:
+  MemoryFileAtOffset() = default;
+  virtual ~MemoryFileAtOffset();
+
+  bool Init(const std::string& file, uint64_t offset);
+
+  bool Read(uint64_t addr, void* dst, size_t size) override;
+
+ protected:
+  size_t size_ = 0;
+  size_t offset_ = 0;
+  uint8_t* data_ = nullptr;
+};
+
+class MemoryOffline : public MemoryFileAtOffset {
+ public:
+  MemoryOffline() = default;
+  virtual ~MemoryOffline() = default;
+
+  bool Init(const std::string& file, uint64_t offset);
+
+  bool Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+  uint64_t start_;
+};
+
+class MemoryRemote : public Memory {
+ public:
+  MemoryRemote(pid_t pid) : pid_(pid) {}
+  virtual ~MemoryRemote() = default;
+
+  bool Read(uint64_t addr, void* dst, size_t size) override;
+
+  pid_t pid() { return pid_; }
+
+ private:
+  pid_t pid_;
+};
+
+class MemoryLocal : public Memory {
+ public:
+  MemoryLocal() = default;
+  virtual ~MemoryLocal() = default;
+
+  bool Read(uint64_t addr, void* dst, size_t size) override;
+};
+
+class MemoryRange : public Memory {
+ public:
+  MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
+      : memory_(memory), begin_(begin), length_(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;
+  }
+
+ private:
+  Memory* memory_;
+  uint64_t begin_;
+  uint64_t length_;
+};
+
+#endif  // _LIBUNWINDSTACK_MEMORY_H
diff --git a/libunwindstack/Regs.h b/libunwindstack/Regs.h
new file mode 100644
index 0000000..2766c6f
--- /dev/null
+++ b/libunwindstack/Regs.h
@@ -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.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_H
+#define _LIBUNWINDSTACK_REGS_H
+
+#include <stdint.h>
+
+#include <vector>
+
+class Regs {
+ public:
+  Regs(uint16_t pc_reg, uint16_t sp_reg, uint16_t total_regs)
+      : pc_reg_(pc_reg), sp_reg_(sp_reg), total_regs_(total_regs) {
+  }
+  virtual ~Regs() = default;
+
+  uint16_t pc_reg() { return pc_reg_; }
+  uint16_t sp_reg() { return sp_reg_; }
+  uint16_t total_regs() { return total_regs_; }
+
+  virtual void* raw_data() = 0;
+  virtual uint64_t pc() = 0;
+  virtual uint64_t sp() = 0;
+
+ protected:
+  uint16_t pc_reg_;
+  uint16_t sp_reg_;
+  uint16_t total_regs_;
+};
+
+template <typename AddressType>
+class RegsTmpl : public Regs {
+ public:
+  RegsTmpl(uint16_t pc_reg, uint16_t sp_reg, uint16_t total_regs)
+      : Regs(pc_reg, sp_reg, total_regs), regs_(total_regs) {}
+  virtual ~RegsTmpl() = default;
+
+  uint64_t pc() override { return regs_[pc_reg_]; }
+  uint64_t sp() override { return regs_[sp_reg_]; }
+
+  inline AddressType& operator[](size_t reg) { return regs_[reg]; }
+
+  void* raw_data() override { return regs_.data(); }
+
+ private:
+  std::vector<AddressType> regs_;
+};
+
+class Regs32 : public RegsTmpl<uint32_t> {
+ public:
+  Regs32(uint16_t pc_reg, uint16_t sp_reg, uint16_t total_regs)
+      : RegsTmpl(pc_reg, sp_reg, total_regs) {}
+  virtual ~Regs32() = default;
+};
+
+class Regs64 : public RegsTmpl<uint64_t> {
+ public:
+  Regs64(uint16_t pc_reg, uint16_t sp_reg, uint16_t total_regs)
+      : RegsTmpl(pc_reg, sp_reg, total_regs) {}
+  virtual ~Regs64() = default;
+};
+
+#endif  // _LIBUNWINDSTACK_REGS_H
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
new file mode 100644
index 0000000..9ea917a
--- /dev/null
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -0,0 +1,992 @@
+/*
+ * 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 <deque>
+#include <ios>
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "ArmExidx.h"
+#include "Log.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+class ArmExidxDecodeTest : public ::testing::TestWithParam<std::string> {
+ protected:
+  void Init(Memory* process_memory = nullptr) {
+    TearDown();
+
+    if (process_memory == nullptr) {
+      process_memory = &process_memory_;
+    }
+
+    regs32_.reset(new Regs32(0, 1, 32));
+    for (size_t i = 0; i < 32; i++) {
+      (*regs32_)[i] = 0;
+    }
+
+    exidx_.reset(new ArmExidx(regs32_.get(), &elf_memory_, process_memory));
+    if (log_) {
+      exidx_->set_log(true);
+      exidx_->set_log_indent(0);
+      exidx_->set_log_skip_execution(false);
+    }
+    data_ = exidx_->data();
+    exidx_->set_cfa(0x10000);
+  }
+
+  void SetUp() override {
+    if (GetParam() != "no_logging") {
+      log_ = false;
+    } else {
+      log_ = true;
+    }
+    ResetLogs();
+    elf_memory_.Clear();
+    process_memory_.Clear();
+    Init();
+  }
+
+  std::unique_ptr<ArmExidx> exidx_;
+  std::unique_ptr<Regs32> regs32_;
+  std::deque<uint8_t>* data_;
+
+  MemoryFake elf_memory_;
+  MemoryFake process_memory_;
+  bool log_;
+};
+
+TEST_P(ArmExidxDecodeTest, vsp_incr) {
+  // 00xxxxxx: vsp = vsp + (xxxxxx << 2) + 4
+  data_->push_back(0x00);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp + 4\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10004U, exidx_->cfa());
+
+  ResetLogs();
+  data_->clear();
+  data_->push_back(0x01);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp + 8\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1000cU, exidx_->cfa());
+
+  ResetLogs();
+  data_->clear();
+  data_->push_back(0x3f);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp + 256\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1010cU, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, vsp_decr) {
+  // 01xxxxxx: vsp = vsp - (xxxxxx << 2) + 4
+  data_->push_back(0x40);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp - 4\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0xfffcU, exidx_->cfa());
+
+  ResetLogs();
+  data_->clear();
+  data_->push_back(0x41);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp - 8\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0xfff4U, exidx_->cfa());
+
+  ResetLogs();
+  data_->clear();
+  data_->push_back(0x7f);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp - 256\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0xfef4U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, refuse_unwind) {
+  // 10000000 00000000: Refuse to unwind
+  data_->push_back(0x80);
+  data_->push_back(0x00);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind Refuse to unwind\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_up_to_12) {
+  // 1000iiii iiiiiiii: Pop up to 12 integer registers
+  data_->push_back(0x80);
+  data_->push_back(0x01);
+  process_memory_.SetData(0x10000, 0x10);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10004U, exidx_->cfa());
+  ASSERT_EQ(0x10U, (*exidx_->regs())[4]);
+
+  ResetLogs();
+  data_->push_back(0x8f);
+  data_->push_back(0xff);
+  for (size_t i = 0; i < 12; i++) {
+    process_memory_.SetData(0x10004 + i * 4, i + 0x20);
+  }
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}\n",
+              GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  // Popping r13 results in a modified cfa.
+  ASSERT_EQ(0x29U, exidx_->cfa());
+
+  ASSERT_EQ(0x20U, (*exidx_->regs())[4]);
+  ASSERT_EQ(0x21U, (*exidx_->regs())[5]);
+  ASSERT_EQ(0x22U, (*exidx_->regs())[6]);
+  ASSERT_EQ(0x23U, (*exidx_->regs())[7]);
+  ASSERT_EQ(0x24U, (*exidx_->regs())[8]);
+  ASSERT_EQ(0x25U, (*exidx_->regs())[9]);
+  ASSERT_EQ(0x26U, (*exidx_->regs())[10]);
+  ASSERT_EQ(0x27U, (*exidx_->regs())[11]);
+  ASSERT_EQ(0x28U, (*exidx_->regs())[12]);
+  ASSERT_EQ(0x29U, (*exidx_->regs())[13]);
+  ASSERT_EQ(0x2aU, (*exidx_->regs())[14]);
+  ASSERT_EQ(0x2bU, (*exidx_->regs())[15]);
+
+  ResetLogs();
+  exidx_->set_cfa(0x10034);
+  data_->push_back(0x81);
+  data_->push_back(0x28);
+  process_memory_.SetData(0x10034, 0x11);
+  process_memory_.SetData(0x10038, 0x22);
+  process_memory_.SetData(0x1003c, 0x33);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r7, r9, r12}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10040U, exidx_->cfa());
+  ASSERT_EQ(0x11U, (*exidx_->regs())[7]);
+  ASSERT_EQ(0x22U, (*exidx_->regs())[9]);
+  ASSERT_EQ(0x33U, (*exidx_->regs())[12]);
+}
+
+TEST_P(ArmExidxDecodeTest, set_vsp_from_register) {
+  // 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
+  exidx_->set_cfa(0x100);
+  for (size_t i = 0; i < 15; i++) {
+    (*regs32_)[i] = i + 1;
+  }
+
+  data_->push_back(0x90);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = r0\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(1U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0x93);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = r3\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(4U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0x9e);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = r14\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(15U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, reserved_prefix) {
+  // 10011101: Reserved as prefix for ARM register to register moves
+  data_->push_back(0x9d);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
+
+  // 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
+  ResetLogs();
+  data_->push_back(0x9f);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_registers) {
+  // 10100nnn: Pop r4-r[4+nnn]
+  data_->push_back(0xa0);
+  process_memory_.SetData(0x10000, 0x14);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10004U, exidx_->cfa());
+  ASSERT_EQ(0x14U, (*exidx_->regs())[4]);
+
+  ResetLogs();
+  data_->push_back(0xa3);
+  process_memory_.SetData(0x10004, 0x20);
+  process_memory_.SetData(0x10008, 0x30);
+  process_memory_.SetData(0x1000c, 0x40);
+  process_memory_.SetData(0x10010, 0x50);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r4-r7}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10014U, exidx_->cfa());
+  ASSERT_EQ(0x20U, (*exidx_->regs())[4]);
+  ASSERT_EQ(0x30U, (*exidx_->regs())[5]);
+  ASSERT_EQ(0x40U, (*exidx_->regs())[6]);
+  ASSERT_EQ(0x50U, (*exidx_->regs())[7]);
+
+  ResetLogs();
+  data_->push_back(0xa7);
+  process_memory_.SetData(0x10014, 0x41);
+  process_memory_.SetData(0x10018, 0x51);
+  process_memory_.SetData(0x1001c, 0x61);
+  process_memory_.SetData(0x10020, 0x71);
+  process_memory_.SetData(0x10024, 0x81);
+  process_memory_.SetData(0x10028, 0x91);
+  process_memory_.SetData(0x1002c, 0xa1);
+  process_memory_.SetData(0x10030, 0xb1);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r4-r11}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10034U, exidx_->cfa());
+  ASSERT_EQ(0x41U, (*exidx_->regs())[4]);
+  ASSERT_EQ(0x51U, (*exidx_->regs())[5]);
+  ASSERT_EQ(0x61U, (*exidx_->regs())[6]);
+  ASSERT_EQ(0x71U, (*exidx_->regs())[7]);
+  ASSERT_EQ(0x81U, (*exidx_->regs())[8]);
+  ASSERT_EQ(0x91U, (*exidx_->regs())[9]);
+  ASSERT_EQ(0xa1U, (*exidx_->regs())[10]);
+  ASSERT_EQ(0xb1U, (*exidx_->regs())[11]);
+}
+
+TEST_P(ArmExidxDecodeTest, pop_registers_with_r14) {
+  // 10101nnn: Pop r4-r[4+nnn], r14
+  data_->push_back(0xa8);
+  process_memory_.SetData(0x10000, 0x12);
+  process_memory_.SetData(0x10004, 0x22);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r4, r14}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10008U, exidx_->cfa());
+  ASSERT_EQ(0x12U, (*exidx_->regs())[4]);
+  ASSERT_EQ(0x22U, (*exidx_->regs())[14]);
+
+  ResetLogs();
+  data_->push_back(0xab);
+  process_memory_.SetData(0x10008, 0x1);
+  process_memory_.SetData(0x1000c, 0x2);
+  process_memory_.SetData(0x10010, 0x3);
+  process_memory_.SetData(0x10014, 0x4);
+  process_memory_.SetData(0x10018, 0x5);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r4-r7, r14}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1001cU, exidx_->cfa());
+  ASSERT_EQ(0x1U, (*exidx_->regs())[4]);
+  ASSERT_EQ(0x2U, (*exidx_->regs())[5]);
+  ASSERT_EQ(0x3U, (*exidx_->regs())[6]);
+  ASSERT_EQ(0x4U, (*exidx_->regs())[7]);
+  ASSERT_EQ(0x5U, (*exidx_->regs())[14]);
+
+  ResetLogs();
+  data_->push_back(0xaf);
+  process_memory_.SetData(0x1001c, 0x1a);
+  process_memory_.SetData(0x10020, 0x2a);
+  process_memory_.SetData(0x10024, 0x3a);
+  process_memory_.SetData(0x10028, 0x4a);
+  process_memory_.SetData(0x1002c, 0x5a);
+  process_memory_.SetData(0x10030, 0x6a);
+  process_memory_.SetData(0x10034, 0x7a);
+  process_memory_.SetData(0x10038, 0x8a);
+  process_memory_.SetData(0x1003c, 0x9a);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r4-r11, r14}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10040U, exidx_->cfa());
+  ASSERT_EQ(0x1aU, (*exidx_->regs())[4]);
+  ASSERT_EQ(0x2aU, (*exidx_->regs())[5]);
+  ASSERT_EQ(0x3aU, (*exidx_->regs())[6]);
+  ASSERT_EQ(0x4aU, (*exidx_->regs())[7]);
+  ASSERT_EQ(0x5aU, (*exidx_->regs())[8]);
+  ASSERT_EQ(0x6aU, (*exidx_->regs())[9]);
+  ASSERT_EQ(0x7aU, (*exidx_->regs())[10]);
+  ASSERT_EQ(0x8aU, (*exidx_->regs())[11]);
+  ASSERT_EQ(0x9aU, (*exidx_->regs())[14]);
+}
+
+TEST_P(ArmExidxDecodeTest, finish) {
+  // 10110000: Finish
+  data_->push_back(0xb0);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind finish\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10000U, exidx_->cfa());
+  ASSERT_EQ(ARM_STATUS_FINISH, exidx_->status());
+}
+
+TEST_P(ArmExidxDecodeTest, spare) {
+  // 10110001 00000000: Spare
+  data_->push_back(0xb1);
+  data_->push_back(0x00);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10000U, exidx_->cfa());
+  ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+
+  // 10110001 xxxxyyyy: Spare (xxxx != 0000)
+  for (size_t x = 1; x < 16; x++) {
+    for (size_t y = 0; y < 16; y++) {
+      ResetLogs();
+      data_->push_back(0xb1);
+      data_->push_back((x << 4) | y);
+      ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
+      ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
+      if (log_) {
+        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+      } else {
+        ASSERT_EQ("", GetFakeLogPrint());
+      }
+      ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
+      ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+    }
+  }
+
+  // 101101nn: Spare
+  for (size_t n = 0; n < 4; n++) {
+    ResetLogs();
+    data_->push_back(0xb4 | n);
+    ASSERT_FALSE(exidx_->Decode()) << "n = " << n;
+    ASSERT_EQ("", GetFakeLogBuf()) << "n = " << n;
+    if (log_) {
+      ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "n = " << n;
+    } else {
+      ASSERT_EQ("", GetFakeLogPrint());
+    }
+    ASSERT_EQ(0x10000U, exidx_->cfa()) << "n = " << n;
+    ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+  }
+
+  // 11000111 00000000: Spare
+  ResetLogs();
+  data_->push_back(0xc7);
+  data_->push_back(0x00);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10000U, exidx_->cfa());
+  ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+
+  // 11000111 xxxxyyyy: Spare (xxxx != 0000)
+  for (size_t x = 1; x < 16; x++) {
+    for (size_t y = 0; y < 16; y++) {
+      ResetLogs();
+      data_->push_back(0xc7);
+      data_->push_back(0x10);
+      ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
+      ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
+      if (log_) {
+        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+      } else {
+        ASSERT_EQ("", GetFakeLogPrint());
+      }
+      ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
+      ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+    }
+  }
+
+  // 11001yyy: Spare (yyy != 000, 001)
+  for (size_t y = 2; y < 8; y++) {
+    ResetLogs();
+    data_->push_back(0xc8 | y);
+    ASSERT_FALSE(exidx_->Decode()) << "y = " << y;
+    ASSERT_EQ("", GetFakeLogBuf()) << "y = " << y;
+    if (log_) {
+      ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "y = " << y;
+    } else {
+      ASSERT_EQ("", GetFakeLogPrint());
+    }
+    ASSERT_EQ(0x10000U, exidx_->cfa()) << "y = " << y;
+    ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+  }
+
+  // 11xxxyyy: Spare (xxx != 000, 001, 010)
+  for (size_t x = 3; x < 8; x++) {
+    for (size_t y = 0; y < 8; y++) {
+      ResetLogs();
+      data_->push_back(0xc0 | (x << 3) | y);
+      ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
+      ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
+      if (log_) {
+        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+      } else {
+        ASSERT_EQ("", GetFakeLogPrint());
+      }
+      ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
+      ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+    }
+  }
+}
+
+TEST_P(ArmExidxDecodeTest, pop_registers_under_mask) {
+  // 10110001 0000iiii: Pop integer registers {r0, r1, r2, r3}
+  data_->push_back(0xb1);
+  data_->push_back(0x01);
+  process_memory_.SetData(0x10000, 0x45);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r0}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10004U, exidx_->cfa());
+  ASSERT_EQ(0x45U, (*exidx_->regs())[0]);
+
+  ResetLogs();
+  data_->push_back(0xb1);
+  data_->push_back(0x0a);
+  process_memory_.SetData(0x10004, 0x23);
+  process_memory_.SetData(0x10008, 0x24);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r1, r3}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1000cU, exidx_->cfa());
+  ASSERT_EQ(0x23U, (*exidx_->regs())[1]);
+  ASSERT_EQ(0x24U, (*exidx_->regs())[3]);
+
+  ResetLogs();
+  data_->push_back(0xb1);
+  data_->push_back(0x0f);
+  process_memory_.SetData(0x1000c, 0x65);
+  process_memory_.SetData(0x10010, 0x54);
+  process_memory_.SetData(0x10014, 0x43);
+  process_memory_.SetData(0x10018, 0x32);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {r0, r1, r2, r3}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1001cU, exidx_->cfa());
+  ASSERT_EQ(0x65U, (*exidx_->regs())[0]);
+  ASSERT_EQ(0x54U, (*exidx_->regs())[1]);
+  ASSERT_EQ(0x43U, (*exidx_->regs())[2]);
+  ASSERT_EQ(0x32U, (*exidx_->regs())[3]);
+}
+
+TEST_P(ArmExidxDecodeTest, vsp_large_incr) {
+  // 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
+  data_->push_back(0xb2);
+  data_->push_back(0x7f);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp + 1024\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10400U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xb2);
+  data_->push_back(0xff);
+  data_->push_back(0x02);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp + 2048\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10c00U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xb2);
+  data_->push_back(0xff);
+  data_->push_back(0x82);
+  data_->push_back(0x30);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind vsp = vsp + 3147776\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x311400U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp_fstmfdx) {
+  // 10110011 sssscccc: Pop VFP double precision registers D[ssss]-D[ssss+cccc] by FSTMFDX
+  data_->push_back(0xb3);
+  data_->push_back(0x00);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1000cU, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xb3);
+  data_->push_back(0x48);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d4-d12}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10058U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp8_fstmfdx) {
+  // 10111nnn: Pop VFP double precision registers D[8]-D[8+nnn] by FSTMFDX
+  data_->push_back(0xb8);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1000cU, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xbb);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d8-d11}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10030U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xbf);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10074U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_mmx_wr10) {
+  // 11000nnn: Intel Wireless MMX pop wR[10]-wR[10+nnn] (nnn != 6, 7)
+  data_->push_back(0xc0);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wR10}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10008U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc2);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wR10-wR12}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10020U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc5);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wR10-wR15}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10050U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_mmx_wr) {
+  // 11000110 sssscccc: Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]
+  data_->push_back(0xc6);
+  data_->push_back(0x00);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wR0}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10008U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc6);
+  data_->push_back(0x25);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wR2-wR7}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10038U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc6);
+  data_->push_back(0xff);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wR15-wR30}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x100b8U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_mmx_wcgr) {
+  // 11000111 0000iiii: Intel Wireless MMX pop wCGR registes {wCGR0,1,2,3}
+  data_->push_back(0xc7);
+  data_->push_back(0x01);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wCGR0}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10004U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc7);
+  data_->push_back(0x0a);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wCGR1, wCGR3}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1000cU, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc7);
+  data_->push_back(0x0f);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {wCGR0, wCGR1, wCGR2, wCGR3}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x1001cU, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp16_vpush) {
+  // 11001000 sssscccc: Pop VFP double precision registers d[16+ssss]-D[16+ssss+cccc] by VPUSH
+  data_->push_back(0xc8);
+  data_->push_back(0x00);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d16}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10008U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc8);
+  data_->push_back(0x14);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d17-d21}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10030U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc8);
+  data_->push_back(0xff);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d31-d46}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x100b0U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp_vpush) {
+  // 11001001 sssscccc: Pop VFP double precision registers d[ssss]-D[ssss+cccc] by VPUSH
+  data_->push_back(0xc9);
+  data_->push_back(0x00);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10008U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc9);
+  data_->push_back(0x23);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d2-d5}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10028U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xc9);
+  data_->push_back(0xff);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d15-d30}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x100a8U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp8_vpush) {
+  // 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
+  data_->push_back(0xd0);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10008U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xd2);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d8-d10}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10020U, exidx_->cfa());
+
+  ResetLogs();
+  data_->push_back(0xd7);
+  ASSERT_TRUE(exidx_->Decode());
+  ASSERT_EQ("", GetFakeLogBuf());
+  if (log_) {
+    ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+  } else {
+    ASSERT_EQ("", GetFakeLogPrint());
+  }
+  ASSERT_EQ(0x10060U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, expect_truncated) {
+  // This test verifies that any op that requires extra ops will
+  // fail if the data is not present.
+  data_->push_back(0x80);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+  data_->clear();
+  data_->push_back(0xb1);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+  data_->clear();
+  data_->push_back(0xb2);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+  data_->clear();
+  data_->push_back(0xb3);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+  data_->clear();
+  data_->push_back(0xc6);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+  data_->clear();
+  data_->push_back(0xc7);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+  data_->clear();
+  data_->push_back(0xc8);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+  data_->clear();
+  data_->push_back(0xc9);
+  ASSERT_FALSE(exidx_->Decode());
+  ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+}
+
+TEST_P(ArmExidxDecodeTest, verify_no_truncated) {
+  // This test verifies that no pattern results in a crash or truncation.
+  MemoryFakeAlwaysReadZero memory_zero;
+  Init(&memory_zero);
+
+  for (size_t x = 0; x < 256; x++) {
+    if (x == 0xb2) {
+      // This opcode is followed by an uleb128, so just skip this one.
+      continue;
+    }
+    for (size_t y = 0; y < 256; y++) {
+      data_->clear();
+      data_->push_back(x);
+      data_->push_back(y);
+      if (!exidx_->Decode()) {
+        ASSERT_NE(ARM_STATUS_TRUNCATED, exidx_->status())
+            << "x y = 0x" << std::hex << x << " 0x" << y;
+        ASSERT_NE(ARM_STATUS_READ_FAILED, exidx_->status())
+            << "x y = 0x" << std::hex << x << " 0x" << y;
+      }
+    }
+  }
+}
+
+INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest, ::testing::Values("logging", "no_logging"));
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
new file mode 100644
index 0000000..021765a
--- /dev/null
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -0,0 +1,331 @@
+/*
+ * 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 <deque>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "ArmExidx.h"
+#include "Log.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+class ArmExidxExtractTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+    elf_memory_.Clear();
+    exidx_ = new ArmExidx(nullptr, &elf_memory_, nullptr);
+    data_ = exidx_->data();
+    data_->clear();
+  }
+
+  void TearDown() override {
+    delete exidx_;
+  }
+
+  ArmExidx* exidx_ = nullptr;
+  std::deque<uint8_t>* data_;
+  MemoryFake elf_memory_;
+};
+
+TEST_F(ArmExidxExtractTest, bad_alignment) {
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x1001));
+  ASSERT_EQ(ARM_STATUS_INVALID_ALIGNMENT, exidx_->status());
+  ASSERT_TRUE(data_->empty());
+}
+
+TEST_F(ArmExidxExtractTest, cant_unwind) {
+  elf_memory_.SetData(0x1000, 0x7fff2340);
+  elf_memory_.SetData(0x1004, 1);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x1000));
+  ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
+  ASSERT_TRUE(data_->empty());
+}
+
+TEST_F(ArmExidxExtractTest, compact) {
+  elf_memory_.SetData(0x4000, 0x7ffa3000);
+  elf_memory_.SetData(0x4004, 0x80a8b0b0);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x4000));
+  ASSERT_EQ(3U, data_->size());
+  ASSERT_EQ(0xa8, data_->at(0));
+  ASSERT_EQ(0xb0, data_->at(1));
+  ASSERT_EQ(0xb0, data_->at(2));
+
+  // Missing finish gets added.
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x534, 0x7ffa3000);
+  elf_memory_.SetData(0x538, 0x80a1a2a3);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x534));
+  ASSERT_EQ(4U, data_->size());
+  ASSERT_EQ(0xa1, data_->at(0));
+  ASSERT_EQ(0xa2, data_->at(1));
+  ASSERT_EQ(0xa3, data_->at(2));
+  ASSERT_EQ(0xb0, data_->at(3));
+}
+
+TEST_F(ArmExidxExtractTest, compact_non_zero_personality) {
+  elf_memory_.SetData(0x4000, 0x7ffa3000);
+
+  uint32_t compact_value = 0x80a8b0b0;
+  for (size_t i = 1; i < 16; i++) {
+    elf_memory_.SetData(0x4004, compact_value | (i << 24));
+    ASSERT_FALSE(exidx_->ExtractEntryData(0x4000));
+    ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
+  }
+}
+
+TEST_F(ArmExidxExtractTest, second_read_compact_personality_1_2) {
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x8100f3b0);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(2U, data_->size());
+  ASSERT_EQ(0xf3, data_->at(0));
+  ASSERT_EQ(0xb0, data_->at(1));
+
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x8200f3f4);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(3U, data_->size());
+  ASSERT_EQ(0xf3, data_->at(0));
+  ASSERT_EQ(0xf4, data_->at(1));
+  ASSERT_EQ(0xb0, data_->at(2));
+
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x8201f3f4);
+  elf_memory_.SetData(0x6238, 0x102030b0);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(6U, data_->size());
+  ASSERT_EQ(0xf3, data_->at(0));
+  ASSERT_EQ(0xf4, data_->at(1));
+  ASSERT_EQ(0x10, data_->at(2));
+  ASSERT_EQ(0x20, data_->at(3));
+  ASSERT_EQ(0x30, data_->at(4));
+  ASSERT_EQ(0xb0, data_->at(5));
+
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x8103f3f4);
+  elf_memory_.SetData(0x6238, 0x10203040);
+  elf_memory_.SetData(0x623c, 0x50607080);
+  elf_memory_.SetData(0x6240, 0x90a0c0d0);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(15U, data_->size());
+  ASSERT_EQ(0xf3, data_->at(0));
+  ASSERT_EQ(0xf4, data_->at(1));
+  ASSERT_EQ(0x10, data_->at(2));
+  ASSERT_EQ(0x20, data_->at(3));
+  ASSERT_EQ(0x30, data_->at(4));
+  ASSERT_EQ(0x40, data_->at(5));
+  ASSERT_EQ(0x50, data_->at(6));
+  ASSERT_EQ(0x60, data_->at(7));
+  ASSERT_EQ(0x70, data_->at(8));
+  ASSERT_EQ(0x80, data_->at(9));
+  ASSERT_EQ(0x90, data_->at(10));
+  ASSERT_EQ(0xa0, data_->at(11));
+  ASSERT_EQ(0xc0, data_->at(12));
+  ASSERT_EQ(0xd0, data_->at(13));
+  ASSERT_EQ(0xb0, data_->at(14));
+}
+
+TEST_F(ArmExidxExtractTest, second_read_compact_personality_illegal) {
+  elf_memory_.SetData(0x5000, 0x7ffa1e48);
+  elf_memory_.SetData(0x5004, 0x1230);
+  elf_memory_.SetData(0x6234, 0x832132b0);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
+
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x5000, 0x7ffa1e48);
+  elf_memory_.SetData(0x5004, 0x1230);
+  elf_memory_.SetData(0x6234, 0x842132b0);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
+}
+
+TEST_F(ArmExidxExtractTest, second_read_offset_is_negative) {
+  elf_memory_.SetData(0x5000, 0x7ffa1e48);
+  elf_memory_.SetData(0x5004, 0x7fffb1e0);
+  elf_memory_.SetData(0x1e4, 0x842132b0);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
+}
+
+TEST_F(ArmExidxExtractTest, second_read_not_compact) {
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x1);
+  elf_memory_.SetData(0x6238, 0x001122b0);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(3U, data_->size());
+  ASSERT_EQ(0x11, data_->at(0));
+  ASSERT_EQ(0x22, data_->at(1));
+  ASSERT_EQ(0xb0, data_->at(2));
+
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x2);
+  elf_memory_.SetData(0x6238, 0x00112233);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(4U, data_->size());
+  ASSERT_EQ(0x11, data_->at(0));
+  ASSERT_EQ(0x22, data_->at(1));
+  ASSERT_EQ(0x33, data_->at(2));
+  ASSERT_EQ(0xb0, data_->at(3));
+
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x3);
+  elf_memory_.SetData(0x6238, 0x01112233);
+  elf_memory_.SetData(0x623c, 0x445566b0);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(7U, data_->size());
+  ASSERT_EQ(0x11, data_->at(0));
+  ASSERT_EQ(0x22, data_->at(1));
+  ASSERT_EQ(0x33, data_->at(2));
+  ASSERT_EQ(0x44, data_->at(3));
+  ASSERT_EQ(0x55, data_->at(4));
+  ASSERT_EQ(0x66, data_->at(5));
+  ASSERT_EQ(0xb0, data_->at(6));
+
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x3);
+  elf_memory_.SetData(0x6238, 0x05112233);
+  elf_memory_.SetData(0x623c, 0x01020304);
+  elf_memory_.SetData(0x6240, 0x05060708);
+  elf_memory_.SetData(0x6244, 0x090a0b0c);
+  elf_memory_.SetData(0x6248, 0x0d0e0f10);
+  elf_memory_.SetData(0x624c, 0x11121314);
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(24U, data_->size());
+  ASSERT_EQ(0x11, data_->at(0));
+  ASSERT_EQ(0x22, data_->at(1));
+  ASSERT_EQ(0x33, data_->at(2));
+  ASSERT_EQ(0x01, data_->at(3));
+  ASSERT_EQ(0x02, data_->at(4));
+  ASSERT_EQ(0x03, data_->at(5));
+  ASSERT_EQ(0x04, data_->at(6));
+  ASSERT_EQ(0x05, data_->at(7));
+  ASSERT_EQ(0x06, data_->at(8));
+  ASSERT_EQ(0x07, data_->at(9));
+  ASSERT_EQ(0x08, data_->at(10));
+  ASSERT_EQ(0x09, data_->at(11));
+  ASSERT_EQ(0x0a, data_->at(12));
+  ASSERT_EQ(0x0b, data_->at(13));
+  ASSERT_EQ(0x0c, data_->at(14));
+  ASSERT_EQ(0x0d, data_->at(15));
+  ASSERT_EQ(0x0e, data_->at(16));
+  ASSERT_EQ(0x0f, data_->at(17));
+  ASSERT_EQ(0x10, data_->at(18));
+  ASSERT_EQ(0x11, data_->at(19));
+  ASSERT_EQ(0x12, data_->at(20));
+  ASSERT_EQ(0x13, data_->at(21));
+  ASSERT_EQ(0x14, data_->at(22));
+  ASSERT_EQ(0xb0, data_->at(23));
+}
+
+TEST_F(ArmExidxExtractTest, read_failures) {
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+
+  elf_memory_.SetData(0x5000, 0x100);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+
+  elf_memory_.SetData(0x5004, 0x100);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+
+  elf_memory_.SetData(0x5104, 0x1);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+
+  elf_memory_.SetData(0x5108, 0x01010203);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+}
+
+TEST_F(ArmExidxExtractTest, malformed) {
+  elf_memory_.SetData(0x5000, 0x100);
+  elf_memory_.SetData(0x5004, 0x100);
+  elf_memory_.SetData(0x5104, 0x1);
+  elf_memory_.SetData(0x5108, 0x06010203);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_MALFORMED, exidx_->status());
+
+  elf_memory_.Clear();
+  elf_memory_.SetData(0x5000, 0x100);
+  elf_memory_.SetData(0x5004, 0x100);
+  elf_memory_.SetData(0x5104, 0x1);
+  elf_memory_.SetData(0x5108, 0x81060203);
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ(ARM_STATUS_MALFORMED, exidx_->status());
+}
+
+TEST_F(ArmExidxExtractTest, cant_unwind_log) {
+  elf_memory_.SetData(0x1000, 0x7fff2340);
+  elf_memory_.SetData(0x1004, 1);
+
+  exidx_->set_log(true);
+  exidx_->set_log_indent(0);
+  exidx_->set_log_skip_execution(false);
+
+  ASSERT_FALSE(exidx_->ExtractEntryData(0x1000));
+  ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
+
+  ASSERT_EQ("4 unwind Raw Data: 0x00 0x00 0x00 0x01\n"
+            "4 unwind [cantunwind]\n", GetFakeLogPrint());
+}
+
+TEST_F(ArmExidxExtractTest, raw_data_compact) {
+  elf_memory_.SetData(0x4000, 0x7ffa3000);
+  elf_memory_.SetData(0x4004, 0x80a8b0b0);
+
+  exidx_->set_log(true);
+  exidx_->set_log_indent(0);
+  exidx_->set_log_skip_execution(false);
+
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x4000));
+  ASSERT_EQ("4 unwind Raw Data: 0xa8 0xb0 0xb0\n", GetFakeLogPrint());
+}
+
+TEST_F(ArmExidxExtractTest, raw_data_non_compact) {
+  elf_memory_.SetData(0x5000, 0x1234);
+  elf_memory_.SetData(0x5004, 0x00001230);
+  elf_memory_.SetData(0x6234, 0x2);
+  elf_memory_.SetData(0x6238, 0x00112233);
+
+  exidx_->set_log(true);
+  exidx_->set_log_indent(0);
+  exidx_->set_log_skip_execution(false);
+
+  ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+  ASSERT_EQ("4 unwind Raw Data: 0x11 0x22 0x33 0xb0\n", GetFakeLogPrint());
+}
diff --git a/libunwindstack/tests/LogFake.cpp b/libunwindstack/tests/LogFake.cpp
new file mode 100644
index 0000000..411594a
--- /dev/null
+++ b/libunwindstack/tests/LogFake.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+
+#include "LogFake.h"
+
+// Forward declarations.
+class Backtrace;
+struct EventTagMap;
+struct AndroidLogEntry;
+
+std::string g_fake_log_buf;
+
+std::string g_fake_log_print;
+
+void ResetLogs() {
+  g_fake_log_buf = "";
+  g_fake_log_print = "";
+}
+
+std::string GetFakeLogBuf() {
+  return g_fake_log_buf;
+}
+
+std::string GetFakeLogPrint() {
+  return g_fake_log_print;
+}
+
+extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
+  g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
+  g_fake_log_buf += tag;
+  g_fake_log_buf += ' ';
+  g_fake_log_buf += msg;
+  return 1;
+}
+
+extern "C" int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  int val = __android_log_vprint(prio, tag, fmt, ap);
+  va_end(ap);
+
+  return val;
+}
+
+extern "C" int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) {
+  g_fake_log_print += std::to_string(prio) + ' ';
+  g_fake_log_print += tag;
+  g_fake_log_print += ' ';
+
+  android::base::StringAppendV(&g_fake_log_print, fmt, ap);
+
+  g_fake_log_print += '\n';
+
+  return 1;
+}
+
+extern "C" log_id_t android_name_to_log_id(const char*) {
+  return LOG_ID_SYSTEM;
+}
+
+extern "C" struct logger_list* android_logger_list_open(log_id_t, int, unsigned int, pid_t) {
+  errno = EACCES;
+  return nullptr;
+}
+
+extern "C" int android_logger_list_read(struct logger_list*, struct log_msg*) {
+  return 0;
+}
+
+extern "C" EventTagMap* android_openEventTagMap(const char*) {
+  return nullptr;
+}
+
+extern "C" int android_log_processBinaryLogBuffer(
+    struct logger_entry*,
+    AndroidLogEntry*, const EventTagMap*, char*, int) {
+  return 0;
+}
+
+extern "C" void android_logger_list_free(struct logger_list*) {
+}
diff --git a/debuggerd/test/selinux_fake.cpp b/libunwindstack/tests/LogFake.h
similarity index 72%
rename from debuggerd/test/selinux_fake.cpp
rename to libunwindstack/tests/LogFake.h
index acdd0a9..006d393 100644
--- a/debuggerd/test/selinux_fake.cpp
+++ b/libunwindstack/tests/LogFake.h
@@ -14,6 +14,13 @@
  * limitations under the License.
  */
 
-extern "C" int selinux_android_restorecon(const char*, unsigned int) {
-  return 0;
-}
+#ifndef _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
+
+#include <string>
+
+void ResetLogs();
+std::string GetFakeLogBuf();
+std::string GetFakeLogPrint();
+
+#endif  // _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
new file mode 100644
index 0000000..216873f
--- /dev/null
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -0,0 +1,219 @@
+/*
+ * 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"
+
+#include "LogFake.h"
+
+class MapsTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+  }
+};
+
+TEST_F(MapsTest, parse_permissions) {
+  MapsBuffer 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_F(MapsTest, parse_name) {
+  MapsBuffer 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_F(MapsTest, parse_offset) {
+  MapsBuffer 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_F(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()));
+
+  MapsFile 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_F(MapsTest, find) {
+  MapsBuffer 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/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp
new file mode 100644
index 0000000..afb1029
--- /dev/null
+++ b/libunwindstack/tests/MemoryFake.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+
+#include "MemoryFake.h"
+
+void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
+  const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
+  for (size_t i = 0; i < length; i++, addr++) {
+    auto value = data_.find(addr);
+    if (value != data_.end()) {
+      value->second = src[i];
+    } else {
+      data_.insert({ addr, src[i] });
+    }
+  }
+}
+
+bool MemoryFake::Read(uint64_t addr, void* memory, size_t size) {
+  uint8_t* dst = reinterpret_cast<uint8_t*>(memory);
+  for (size_t i = 0; i < size; i++, addr++) {
+    auto value = data_.find(addr);
+    if (value == data_.end()) {
+      return false;
+    }
+    dst[i] = value->second;
+  }
+  return true;
+}
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
new file mode 100644
index 0000000..4f898fa
--- /dev/null
+++ b/libunwindstack/tests/MemoryFake.h
@@ -0,0 +1,66 @@
+/*
+ * 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_MEMORY_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+#include "Memory.h"
+
+class MemoryFake : public Memory {
+ public:
+  MemoryFake() = default;
+  virtual ~MemoryFake() = default;
+
+  bool Read(uint64_t addr, void* buffer, size_t size) override;
+
+  void SetMemory(uint64_t addr, const void* memory, size_t length);
+
+  void SetData(uint64_t addr, uint32_t value) {
+    SetMemory(addr, &value, sizeof(value));
+  }
+
+  void SetMemory(uint64_t addr, std::vector<uint8_t> values) {
+    SetMemory(addr, values.data(), values.size());
+  }
+
+  void SetMemory(uint64_t addr, std::string string) {
+    SetMemory(addr, string.c_str(), string.size() + 1);
+  }
+
+  void Clear() { data_.clear(); }
+
+ private:
+  std::unordered_map<uint64_t, uint8_t> data_;
+};
+
+class MemoryFakeAlwaysReadZero : public Memory {
+ public:
+  MemoryFakeAlwaysReadZero() = default;
+  virtual ~MemoryFakeAlwaysReadZero() = default;
+
+  bool Read(uint64_t, void* buffer, size_t size) override {
+    memset(buffer, 0, size);
+    return true;
+  }
+};
+
+#endif  // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
new file mode 100644
index 0000000..ebc6118
--- /dev/null
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -0,0 +1,167 @@
+/*
+ * 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 <android-base/test_utils.h>
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "LogFake.h"
+
+class MemoryFileTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+    tf_ = new TemporaryFile;
+  }
+
+  void TearDown() override {
+    delete tf_;
+  }
+
+  void WriteTestData() {
+    ASSERT_TRUE(android::base::WriteStringToFd("0123456789abcdefghijklmnopqrstuvxyz", tf_->fd));
+  }
+
+  MemoryFileAtOffset memory_;
+
+  TemporaryFile* tf_ = nullptr;
+};
+
+TEST_F(MemoryFileTest, offset_0) {
+  WriteTestData();
+
+  ASSERT_TRUE(memory_.Init(tf_->path, 0));
+  std::vector<char> buffer(11);
+  ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+  buffer[10] = '\0';
+  ASSERT_STREQ("0123456789", buffer.data());
+}
+
+TEST_F(MemoryFileTest, offset_non_zero) {
+  WriteTestData();
+
+  ASSERT_TRUE(memory_.Init(tf_->path, 10));
+  std::vector<char> buffer(11);
+  ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+  buffer[10] = '\0';
+  ASSERT_STREQ("abcdefghij", buffer.data());
+}
+
+TEST_F(MemoryFileTest, offset_non_zero_larger_than_pagesize) {
+  size_t pagesize = getpagesize();
+  std::string large_string;
+  for (size_t i = 0; i < pagesize; i++) {
+    large_string += '1';
+  }
+  large_string += "012345678901234abcdefgh";
+  ASSERT_TRUE(android::base::WriteStringToFd(large_string, tf_->fd));
+
+  ASSERT_TRUE(memory_.Init(tf_->path, pagesize + 15));
+  std::vector<char> buffer(9);
+  ASSERT_TRUE(memory_.Read(0, buffer.data(), 8));
+  buffer[8] = '\0';
+  ASSERT_STREQ("abcdefgh", buffer.data());
+}
+
+TEST_F(MemoryFileTest, offset_pagesize_aligned) {
+  size_t pagesize = getpagesize();
+  std::string data;
+  for (size_t i = 0; i < 2 * pagesize; i++) {
+    data += static_cast<char>((i / pagesize) + '0');
+    data += static_cast<char>((i % 10) + '0');
+  }
+  ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+  ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize));
+  std::vector<char> buffer(11);
+  ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+  buffer[10] = '\0';
+  std::string expected_str;
+  for (size_t i = 0; i < 5; i++) {
+    expected_str += '1';
+    expected_str += static_cast<char>(((i + pagesize) % 10) + '0');
+  }
+  ASSERT_STREQ(expected_str.c_str(), buffer.data());
+}
+
+TEST_F(MemoryFileTest, offset_pagesize_aligned_plus_extra) {
+  size_t pagesize = getpagesize();
+  std::string data;
+  for (size_t i = 0; i < 2 * pagesize; i++) {
+    data += static_cast<char>((i / pagesize) + '0');
+    data += static_cast<char>((i % 10) + '0');
+  }
+  ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+  ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize + 10));
+  std::vector<char> buffer(11);
+  ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+  buffer[10] = '\0';
+  std::string expected_str;
+  for (size_t i = 0; i < 5; i++) {
+    expected_str += '1';
+    expected_str += static_cast<char>(((i + pagesize + 5) % 10) + '0');
+  }
+  ASSERT_STREQ(expected_str.c_str(), buffer.data());
+}
+
+TEST_F(MemoryFileTest, read_error) {
+  std::string data;
+  for (size_t i = 0; i < 5000; i++) {
+    data += static_cast<char>((i % 10) + '0');
+  }
+  ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+
+  std::vector<char> buffer(100);
+
+  // Read before init.
+  ASSERT_FALSE(memory_.Read(0, buffer.data(), 10));
+
+  ASSERT_TRUE(memory_.Init(tf_->path, 0));
+
+  ASSERT_FALSE(memory_.Read(10000, buffer.data(), 10));
+  ASSERT_FALSE(memory_.Read(5000, buffer.data(), 10));
+  ASSERT_FALSE(memory_.Read(4990, buffer.data(), 11));
+  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));
+}
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
new file mode 100644
index 0000000..49ece9d
--- /dev/null
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 <vector>
+
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "LogFake.h"
+
+class MemoryLocalTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+  }
+};
+
+TEST_F(MemoryLocalTest, read) {
+  std::vector<uint8_t> src(1024);
+  memset(src.data(), 0x4c, 1024);
+
+  MemoryLocal local;
+
+  std::vector<uint8_t> dst(1024);
+  ASSERT_TRUE(local.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+  ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024));
+  for (size_t i = 0; i < 1024; i++) {
+    ASSERT_EQ(0x4cU, dst[i]);
+  }
+
+  memset(src.data(), 0x23, 512);
+  ASSERT_TRUE(local.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+  ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024));
+  for (size_t i = 0; i < 512; i++) {
+    ASSERT_EQ(0x23U, dst[i]);
+  }
+  for (size_t i = 512; i < 1024; i++) {
+    ASSERT_EQ(0x4cU, dst[i]);
+  }
+}
+
+TEST_F(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_F(MemoryLocalTest, read_illegal) {
+  MemoryLocal local;
+
+  std::vector<uint8_t> dst(100);
+  ASSERT_FALSE(local.Read(0, dst.data(), 1));
+  ASSERT_FALSE(local.Read(0, dst.data(), 100));
+}
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
new file mode 100644
index 0000000..fcae3a4
--- /dev/null
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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 <vector>
+
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+class MemoryRangeTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+    memory_ = new MemoryFake;
+  }
+
+  MemoryFake* memory_;
+};
+
+TEST_F(MemoryRangeTest, read) {
+  std::vector<uint8_t> src(1024);
+  memset(src.data(), 0x4c, 1024);
+  memory_->SetMemory(9001, src);
+
+  MemoryRange range(memory_, 9001, 9001 + src.size());
+
+  std::vector<uint8_t> dst(1024);
+  ASSERT_TRUE(range.Read(0, dst.data(), src.size()));
+  for (size_t i = 0; i < 1024; i++) {
+    ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+  }
+}
+
+TEST_F(MemoryRangeTest, read_near_limit) {
+  std::vector<uint8_t> src(4096);
+  memset(src.data(), 0x4c, 4096);
+  memory_->SetMemory(1000, src);
+
+  MemoryRange range(memory_, 1000, 2024);
+
+  std::vector<uint8_t> dst(1024);
+  ASSERT_TRUE(range.Read(1020, dst.data(), 4));
+  for (size_t i = 0; i < 4; i++) {
+    ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+  }
+
+  // Verify that reads outside of the range will fail.
+  ASSERT_FALSE(range.Read(1020, dst.data(), 5));
+  ASSERT_FALSE(range.Read(1024, dst.data(), 1));
+  ASSERT_FALSE(range.Read(1024, dst.data(), 1024));
+}
+
+TEST_F(MemoryRangeTest, read_string_past_end) {
+  std::string name("0123456789");
+  memory_->SetMemory(0, name);
+
+  // 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));
+}
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
new file mode 100644
index 0000000..49244a5
--- /dev/null
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <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/test_utils.h>
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "LogFake.h"
+
+class MemoryRemoteTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+  }
+
+  static uint64_t NanoTime() {
+    struct timespec t = { 0, 0 };
+    clock_gettime(CLOCK_MONOTONIC, &t);
+    return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
+  }
+
+  static bool Attach(pid_t pid) {
+    if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+      return false;
+    }
+
+    uint64_t start = NanoTime();
+    siginfo_t si;
+    while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
+      if ((NanoTime() - start) > 10 * NS_PER_SEC) {
+        printf("%d: Failed to stop after 10 seconds.\n", pid);
+        return false;
+      }
+      usleep(30);
+    }
+    return true;
+  }
+
+  static bool Detach(pid_t pid) {
+    return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
+  }
+
+  static constexpr size_t NS_PER_SEC = 1000000000ULL;
+};
+
+TEST_F(MemoryRemoteTest, read) {
+  std::vector<uint8_t> src(1024);
+  memset(src.data(), 0x4c, 1024);
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true);
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+
+  ASSERT_TRUE(Attach(pid));
+
+  MemoryRemote remote(pid);
+
+  std::vector<uint8_t> dst(1024);
+  ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+  for (size_t i = 0; i < 1024; i++) {
+    ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_TRUE(Detach(pid));
+
+  kill(pid, SIGKILL);
+}
+
+TEST_F(MemoryRemoteTest, read_fail) {
+  int pagesize = getpagesize();
+  void* src = mmap(nullptr, pagesize * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,-1, 0);
+  memset(src, 0x4c, pagesize * 2);
+  ASSERT_NE(MAP_FAILED, src);
+  // Put a hole right after the first page.
+  ASSERT_EQ(0, munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(src) + pagesize),
+                      pagesize));
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true);
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+
+  ASSERT_TRUE(Attach(pid));
+
+  MemoryRemote remote(pid);
+
+  std::vector<uint8_t> dst(pagesize);
+  ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src), dst.data(), pagesize));
+  for (size_t i = 0; i < 1024; i++) {
+    ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_FALSE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize, dst.data(), 1));
+  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));
+
+  ASSERT_EQ(0, munmap(src, pagesize));
+
+  ASSERT_TRUE(Detach(pid));
+
+  kill(pid, SIGKILL);
+}
+
+TEST_F(MemoryRemoteTest, read_illegal) {
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true);
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+
+  ASSERT_TRUE(Attach(pid));
+
+  MemoryRemote remote(pid);
+
+  std::vector<uint8_t> dst(100);
+  ASSERT_FALSE(remote.Read(0, dst.data(), 1));
+  ASSERT_FALSE(remote.Read(0, dst.data(), 100));
+
+  ASSERT_TRUE(Detach(pid));
+
+  kill(pid, SIGKILL);
+}
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
new file mode 100644
index 0000000..f9e8b0e
--- /dev/null
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include "Regs.h"
+
+class RegsTest : public ::testing::Test {};
+
+TEST_F(RegsTest, regs32) {
+  Regs32 regs32(10, 20, 30);
+
+  ASSERT_EQ(10U, regs32.pc_reg());
+  ASSERT_EQ(20U, regs32.sp_reg());
+  ASSERT_EQ(30U, regs32.total_regs());
+
+  uint32_t* raw = reinterpret_cast<uint32_t*>(regs32.raw_data());
+  for (size_t i = 0; i < 30; i++) {
+    raw[i] = 0xf0000000 + i;
+  }
+
+  ASSERT_EQ(0xf000000aU, regs32.pc());
+  ASSERT_EQ(0xf0000014U, regs32.sp());
+
+  ASSERT_EQ(0xf0000001U, regs32[1]);
+  regs32[1] = 10;
+  ASSERT_EQ(10U, regs32[1]);
+
+  ASSERT_EQ(0xf000001dU, regs32[29]);
+}
+
+TEST_F(RegsTest, regs64) {
+  Regs64 regs64(10, 20, 30);
+
+  ASSERT_EQ(10U, regs64.pc_reg());
+  ASSERT_EQ(20U, regs64.sp_reg());
+  ASSERT_EQ(30U, regs64.total_regs());
+
+  uint64_t* raw = reinterpret_cast<uint64_t*>(regs64.raw_data());
+  for (size_t i = 0; i < 30; i++) {
+    raw[i] = 0xf123456780000000UL + i;
+  }
+
+  ASSERT_EQ(0xf12345678000000aUL, regs64.pc());
+  ASSERT_EQ(0xf123456780000014UL, regs64.sp());
+
+  ASSERT_EQ(0xf123456780000008U, regs64[8]);
+  regs64[8] = 10;
+  ASSERT_EQ(10U, regs64[8]);
+
+  ASSERT_EQ(0xf12345678000001dU, regs64[29]);
+}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 217b8c3..0c777b1 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -12,6 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+cc_library_headers {
+    name: "libutils_headers",
+    host_supported: true,
+    export_include_dirs: ["include"],
+    target: {
+        windows: {
+           enabled: true,
+      },
+    },
+}
+
 cc_library {
     name: "libutils",
     host_supported: true,
@@ -42,6 +53,8 @@
 
     cflags: ["-Werror"],
     include_dirs: ["external/safe-iop/include"],
+    header_libs: ["libutils_headers"],
+    export_header_lib_headers: ["libutils_headers"],
 
     arch: {
         mips: {
diff --git a/libutils/BlobCache.cpp b/libutils/BlobCache.cpp
index 126995b..b0fdd44 100644
--- a/libutils/BlobCache.cpp
+++ b/libutils/BlobCache.cpp
@@ -17,13 +17,10 @@
 #define LOG_TAG "BlobCache"
 //#define LOG_NDEBUG 0
 
-#include <inttypes.h>
-#include <stdlib.h>
-#include <string.h>
-
 #include <utils/BlobCache.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
+#include <utils/Timers.h>
+
+#include <inttypes.h>
 
 #include <cutils/properties.h>
 
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index 699da74..bd6015e 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -16,9 +16,10 @@
 
 #define LOG_TAG "CallStack"
 
+#include <utils/CallStack.h>
+
 #include <memory>
 
-#include <utils/CallStack.h>
 #include <utils/Printer.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
diff --git a/libutils/LinearTransform.cpp b/libutils/LinearTransform.cpp
index 138ce8b..d2e91a8 100644
--- a/libutils/LinearTransform.cpp
+++ b/libutils/LinearTransform.cpp
@@ -16,10 +16,9 @@
 
 #define __STDC_LIMIT_MACROS
 
-#include <assert.h>
-#include <stdint.h>
-
 #include <utils/LinearTransform.h>
+#include <assert.h>
+
 
 // disable sanitize as these functions may intentionally overflow (see comments below).
 // the ifdef can be removed when host builds use clang.
diff --git a/libutils/Log.cpp b/libutils/Log.cpp
index bffb56e..2c1fb86 100644
--- a/libutils/Log.cpp
+++ b/libutils/Log.cpp
@@ -21,14 +21,14 @@
 
 namespace android {
 
-LogIfSlow::LogIfSlow(const char* tag, android_LogPriority priority,
-        int timeoutMillis, const char* message) :
-        mTag(tag), mPriority(priority), mTimeoutMillis(timeoutMillis), mMessage(message),
-        mStart(systemTime(SYSTEM_TIME_BOOTTIME)) {
+LogIfSlow::LogIfSlow(
+        const char* tag, android_LogPriority priority, int timeoutMillis, const char* message)
+        : mTag(tag), mPriority(priority), mTimeoutMillis(timeoutMillis), mMessage(message),
+          mStart(systemTime(SYSTEM_TIME_BOOTTIME)) {
 }
 
 LogIfSlow::~LogIfSlow() {
-    int durationMillis = nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_BOOTTIME) - mStart);
+    int durationMillis = (int)nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_BOOTTIME) - mStart);
     if (durationMillis > mTimeoutMillis) {
         LOG_PRI(mPriority, mTag, "%s: %dms", mMessage, durationMillis);
     }
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 3edc536..6c57b2e 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -13,17 +13,8 @@
 // Debugs callback registration and invocation.
 #define DEBUG_CALLBACKS 0
 
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <string.h>
-#include <sys/eventfd.h>
-#include <unistd.h>
-
-#include <android/log.h>
 #include <utils/Looper.h>
-#include <utils/Timers.h>
+#include <sys/eventfd.h>
 
 namespace android {
 
diff --git a/libutils/NativeHandle.cpp b/libutils/NativeHandle.cpp
index e4daca7..97d06b8 100644
--- a/libutils/NativeHandle.cpp
+++ b/libutils/NativeHandle.cpp
@@ -19,14 +19,14 @@
 
 namespace android {
 
-sp<NativeHandle> NativeHandle::create(
-        native_handle_t* handle, bool ownsHandle) {
+sp<NativeHandle> NativeHandle::create(native_handle_t* handle, bool ownsHandle) {
     return handle ? new NativeHandle(handle, ownsHandle) : NULL;
 }
 
 NativeHandle::NativeHandle(native_handle_t* handle, bool ownsHandle)
-:   mHandle(handle), mOwnsHandle(ownsHandle)
-{}
+        : mHandle(handle), mOwnsHandle(ownsHandle) {
+
+}
 
 NativeHandle::~NativeHandle() {
     if (mOwnsHandle) {
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
index 98cd2c6..9573a10 100644
--- a/libutils/Printer.cpp
+++ b/libutils/Printer.cpp
@@ -21,10 +21,6 @@
 #include <utils/String8.h>
 #include <utils/Log.h>
 
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-
 namespace android {
 
 /*
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
index 73ed4eb..1d9f30d 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -17,18 +17,11 @@
 #define LOG_TAG "ProcessCallStack"
 // #define LOG_NDEBUG 0
 
-#include <dirent.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <memory>
-
-#include <utils/Log.h>
-#include <utils/Errors.h>
 #include <utils/ProcessCallStack.h>
-#include <utils/Printer.h>
 
-#include <limits.h>
+#include <dirent.h>
+
+#include <utils/Printer.h>
 
 namespace android {
 
diff --git a/libutils/PropertyMap.cpp b/libutils/PropertyMap.cpp
index 5520702..4bcdd0f 100644
--- a/libutils/PropertyMap.cpp
+++ b/libutils/PropertyMap.cpp
@@ -16,11 +16,7 @@
 
 #define LOG_TAG "PropertyMap"
 
-#include <stdlib.h>
-#include <string.h>
-
 #include <utils/PropertyMap.h>
-#include <utils/Log.h>
 
 // Enables debug output for the parser.
 #define DEBUG_PARSER 0
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 1f8395b..0d98db9 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -17,19 +17,9 @@
 #define LOG_TAG "RefBase"
 // #define LOG_NDEBUG 0
 
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <typeinfo>
-#include <unistd.h>
-
 #include <utils/RefBase.h>
 
 #include <utils/CallStack.h>
-#include <utils/Log.h>
-#include <utils/threads.h>
 
 #ifndef __unused
 #define __unused __attribute__((__unused__))
diff --git a/libutils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp
index 229e3f2..bad98b2 100644
--- a/libutils/SharedBuffer.cpp
+++ b/libutils/SharedBuffer.cpp
@@ -16,12 +16,12 @@
 
 #define LOG_TAG "sharedbuffer"
 
+#include "SharedBuffer.h"
+
 #include <stdlib.h>
 #include <string.h>
 
-#include <android/log.h>
-
-#include "SharedBuffer.h"
+#include <log/log.h>
 
 // ---------------------------------------------------------------------------
 
diff --git a/libutils/StopWatch.cpp b/libutils/StopWatch.cpp
index 8c7b596..219c13c 100644
--- a/libutils/StopWatch.cpp
+++ b/libutils/StopWatch.cpp
@@ -16,9 +16,7 @@
 
 #define LOG_TAG "StopWatch"
 
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
+#include <utils/StopWatch.h>
 
 /* for PRId64 */
 #ifndef __STDC_FORMAT_MACROS
@@ -27,8 +25,6 @@
 #include <inttypes.h>
 
 #include <utils/Log.h>
-#include <utils/Errors.h>
-#include <utils/StopWatch.h>
 
 /*****************************************************************************/
 
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 9f5cfea..12bb7c4 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -17,12 +17,6 @@
 #include <utils/String16.h>
 
 #include <utils/Log.h>
-#include <utils/Unicode.h>
-#include <utils/threads.h>
-
-#include <memory.h>
-#include <stdio.h>
-#include <ctype.h>
 
 #include "SharedBuffer.h"
 
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index cacaf91..0ac39f9 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -21,11 +21,7 @@
 
 #include <utils/Compat.h>
 #include <utils/Log.h>
-#include <utils/Unicode.h>
 #include <utils/String16.h>
-#include <utils/threads.h>
-
-#include <ctype.h>
 
 #include "SharedBuffer.h"
 
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index 965e32c..28fc351 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -19,17 +19,17 @@
  * System clock functions.
  */
 
+#define LOG_TAG "SystemClock"
+
+#include <utils/SystemClock.h>
+
 #include <sys/time.h>
-#include <limits.h>
-#include <fcntl.h>
 #include <string.h>
 #include <errno.h>
 
 #include <cutils/compiler.h>
-#include <utils/SystemClock.h>
-#include <utils/Timers.h>
 
-#define LOG_TAG "SystemClock"
+#include <utils/Timers.h>
 #include <utils/Log.h>
 
 namespace android {
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index def739f..6317c32 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -18,16 +18,10 @@
 #define LOG_TAG "libutils.threads"
 
 #include <assert.h>
-#include <errno.h>
-#include <memory.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+#include <utils/Thread.h>
+#include <utils/AndroidThreads.h>
 
 #if !defined(_WIN32)
-# include <pthread.h>
-# include <sched.h>
 # include <sys/resource.h>
 #else
 # include <windows.h>
@@ -40,7 +34,6 @@
 #include <sys/prctl.h>
 #endif
 
-#include <utils/threads.h>
 #include <utils/Log.h>
 
 #include <cutils/sched_policy.h>
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index 201bc41..b2df9a5 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -20,7 +20,6 @@
 #include <utils/Timers.h>
 
 #include <limits.h>
-#include <sys/time.h>
 #include <time.h>
 
 #if defined(__ANDROID__)
@@ -53,7 +52,7 @@
 
 int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime)
 {
-    int timeoutDelayMillis;
+    nsecs_t timeoutDelayMillis;
     if (timeoutTime > referenceTime) {
         uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime);
         if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) {
@@ -64,5 +63,5 @@
     } else {
         timeoutDelayMillis = 0;
     }
-    return timeoutDelayMillis;
+    return (int)timeoutDelayMillis;
 }
diff --git a/libutils/Tokenizer.cpp b/libutils/Tokenizer.cpp
index 2d0e83d..b68a2cf 100644
--- a/libutils/Tokenizer.cpp
+++ b/libutils/Tokenizer.cpp
@@ -16,14 +16,10 @@
 
 #define LOG_TAG "Tokenizer"
 
-#include <stdlib.h>
-#include <unistd.h>
+#include <utils/Tokenizer.h>
 #include <fcntl.h>
-#include <errno.h>
-#include <sys/types.h>
 #include <sys/stat.h>
 #include <utils/Log.h>
-#include <utils/Tokenizer.h>
 
 // Enables debug output for the tokenizer.
 #define DEBUG_TOKENIZER 0
diff --git a/libutils/Trace.cpp b/libutils/Trace.cpp
index 36fd802..8530fdc 100644
--- a/libutils/Trace.cpp
+++ b/libutils/Trace.cpp
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
-#include <utils/misc.h>
 #include <utils/Trace.h>
+#include <utils/misc.h>
 
 static void traceInit() __attribute__((constructor));
 
-static void traceInit()
-{
+static void traceInit() {
     ::android::add_sysprop_change_callback(atrace_update_tags, 0);
 }
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index c32f462..5fd9155 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -16,11 +16,10 @@
 
 #define LOG_TAG "unicode"
 
-#include <limits.h>
-#include <stddef.h>
-
-#include <android/log.h>
 #include <utils/Unicode.h>
+#include <limits.h>
+
+#include <log/log.h>
 
 #if defined(_WIN32)
 # undef  nhtol
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index 893e4f2..ef3277f 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -16,13 +16,13 @@
 
 #define LOG_TAG "Vector"
 
+#include <utils/VectorImpl.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
-#include <android/log.h>
-#include <utils/Errors.h>
-#include <utils/VectorImpl.h>
+#include <log/log.h>
 
 #include <safe_iop.h>
 
diff --git a/include/utils/AndroidThreads.h b/libutils/include/utils/AndroidThreads.h
similarity index 100%
rename from include/utils/AndroidThreads.h
rename to libutils/include/utils/AndroidThreads.h
diff --git a/include/utils/Atomic.h b/libutils/include/utils/Atomic.h
similarity index 100%
rename from include/utils/Atomic.h
rename to libutils/include/utils/Atomic.h
diff --git a/include/utils/BitSet.h b/libutils/include/utils/BitSet.h
similarity index 100%
rename from include/utils/BitSet.h
rename to libutils/include/utils/BitSet.h
diff --git a/include/utils/BlobCache.h b/libutils/include/utils/BlobCache.h
similarity index 99%
rename from include/utils/BlobCache.h
rename to libutils/include/utils/BlobCache.h
index 65dca9f..27a81e3 100644
--- a/include/utils/BlobCache.h
+++ b/libutils/include/utils/BlobCache.h
@@ -19,10 +19,8 @@
 
 #include <stddef.h>
 
-#include <utils/Flattenable.h>
 #include <utils/RefBase.h>
 #include <utils/SortedVector.h>
-#include <utils/threads.h>
 
 namespace android {
 
diff --git a/include/utils/ByteOrder.h b/libutils/include/utils/ByteOrder.h
similarity index 100%
rename from include/utils/ByteOrder.h
rename to libutils/include/utils/ByteOrder.h
diff --git a/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
similarity index 100%
rename from include/utils/CallStack.h
rename to libutils/include/utils/CallStack.h
diff --git a/include/utils/Compat.h b/libutils/include/utils/Compat.h
similarity index 100%
rename from include/utils/Compat.h
rename to libutils/include/utils/Compat.h
diff --git a/include/utils/Condition.h b/libutils/include/utils/Condition.h
similarity index 81%
rename from include/utils/Condition.h
rename to libutils/include/utils/Condition.h
index 25a53aa..3019a21 100644
--- a/include/utils/Condition.h
+++ b/libutils/include/utils/Condition.h
@@ -41,6 +41,11 @@
  * call wait(), then either re-wait() if things aren't quite what you want,
  * or unlock the mutex and continue.  All threads calling wait() must
  * use the same mutex for a given Condition.
+ *
+ * On Android and Apple platforms, these are implemented as a simple wrapper
+ * around pthread condition variables.  Care must be taken to abide by
+ * the pthreads semantics, in particular, a boolean predicate must
+ * be re-evaluated after a wake-up, as spurious wake-ups may happen.
  */
 class Condition {
 public:
@@ -58,10 +63,11 @@
     explicit Condition(int type);
     ~Condition();
     // Wait on the condition variable.  Lock the mutex before calling.
+    // Note that spurious wake-ups may happen.
     status_t wait(Mutex& mutex);
     // same with relative timeout
     status_t waitRelative(Mutex& mutex, nsecs_t reltime);
-    // Signal the condition variable, allowing exactly one thread to continue.
+    // Signal the condition variable, allowing one thread to continue.
     void signal();
     // Signal the condition variable, allowing one or all threads to continue.
     void signal(WakeUpType type) {
@@ -86,19 +92,22 @@
 
 #if !defined(_WIN32)
 
-inline Condition::Condition() {
-    pthread_cond_init(&mCond, NULL);
+inline Condition::Condition() : Condition(PRIVATE) {
 }
 inline Condition::Condition(int type) {
+    pthread_condattr_t attr;
+    pthread_condattr_init(&attr);
+#if defined(__linux__)
+    pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
+#endif
+
     if (type == SHARED) {
-        pthread_condattr_t attr;
-        pthread_condattr_init(&attr);
         pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
-        pthread_cond_init(&mCond, &attr);
-        pthread_condattr_destroy(&attr);
-    } else {
-        pthread_cond_init(&mCond, NULL);
     }
+
+    pthread_cond_init(&mCond, &attr);
+    pthread_condattr_destroy(&attr);
+
 }
 inline Condition::~Condition() {
     pthread_cond_destroy(&mCond);
@@ -109,7 +118,7 @@
 inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) {
     struct timespec ts;
 #if defined(__linux__)
-    clock_gettime(CLOCK_REALTIME, &ts);
+    clock_gettime(CLOCK_MONOTONIC, &ts);
 #else // __APPLE__
     // Apple doesn't support POSIX clocks.
     struct timeval t;
@@ -139,17 +148,6 @@
     return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts);
 }
 inline void Condition::signal() {
-    /*
-     * POSIX says pthread_cond_signal wakes up "one or more" waiting threads.
-     * However bionic follows the glibc guarantee which wakes up "exactly one"
-     * waiting thread.
-     *
-     * man 3 pthread_cond_signal
-     *   pthread_cond_signal restarts one of the threads that are waiting on
-     *   the condition variable cond. If no threads are waiting on cond,
-     *   nothing happens. If several threads are waiting on cond, exactly one
-     *   is restarted, but it is not specified which.
-     */
     pthread_cond_signal(&mCond);
 }
 inline void Condition::broadcast() {
diff --git a/include/utils/Debug.h b/libutils/include/utils/Debug.h
similarity index 100%
rename from include/utils/Debug.h
rename to libutils/include/utils/Debug.h
diff --git a/include/utils/Endian.h b/libutils/include/utils/Endian.h
similarity index 100%
rename from include/utils/Endian.h
rename to libutils/include/utils/Endian.h
diff --git a/include/utils/Errors.h b/libutils/include/utils/Errors.h
similarity index 100%
rename from include/utils/Errors.h
rename to libutils/include/utils/Errors.h
diff --git a/libutils/include/utils/FastStrcmp.h b/libutils/include/utils/FastStrcmp.h
new file mode 100644
index 0000000..3844e7d
--- /dev/null
+++ b/libutils/include/utils/FastStrcmp.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014-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 _ANDROID_UTILS_FASTSTRCMP_H__
+#define _ANDROID_UTILS_FASTSTRCMP_H__
+
+#ifdef __cplusplus
+
+// Optimized for instruction cache locality
+//
+// Template class fastcmp used to create more time-efficient str*cmp
+// functions by pre-checking the first character before resorting
+// to calling the underlying string function.  Profiled with a
+// measurable speedup when used in hot code.  Usage is of the form:
+//
+//  fastcmp<strncmp>(str1, str2, len)
+//
+// NB: Does not work for the case insensitive str*cmp functions.
+// NB: Returns boolean, do not use if expecting to check negative value.
+//     Thus not semantically identical to the expected function behavior.
+
+template <int (*cmp)(const char *l, const char *r, const size_t s)>
+static inline int fastcmp(const char *l, const char *r, const size_t s) {
+    return (*l != *r) || cmp(l + 1, r + 1, s - 1);
+}
+
+template <int (*cmp)(const void *l, const void *r, const size_t s)>
+static inline int fastcmp(const void *lv, const void *rv, const size_t s) {
+    const char *l = static_cast<const char *>(lv);
+    const char *r = static_cast<const char *>(rv);
+    return (*l != *r) || cmp(l + 1, r + 1, s - 1);
+}
+
+template <int (*cmp)(const char *l, const char *r)>
+static inline int fastcmp(const char *l, const char *r) {
+    return (*l != *r) || cmp(l + 1, r + 1);
+}
+
+#endif
+
+#endif // _ANDROID_UTILS_FASTSTRCMP_H__
diff --git a/include/utils/FileMap.h b/libutils/include/utils/FileMap.h
similarity index 100%
rename from include/utils/FileMap.h
rename to libutils/include/utils/FileMap.h
diff --git a/include/utils/Flattenable.h b/libutils/include/utils/Flattenable.h
similarity index 100%
rename from include/utils/Flattenable.h
rename to libutils/include/utils/Flattenable.h
diff --git a/include/utils/Functor.h b/libutils/include/utils/Functor.h
similarity index 100%
rename from include/utils/Functor.h
rename to libutils/include/utils/Functor.h
diff --git a/include/utils/JenkinsHash.h b/libutils/include/utils/JenkinsHash.h
similarity index 100%
rename from include/utils/JenkinsHash.h
rename to libutils/include/utils/JenkinsHash.h
diff --git a/include/utils/KeyedVector.h b/libutils/include/utils/KeyedVector.h
similarity index 99%
rename from include/utils/KeyedVector.h
rename to libutils/include/utils/KeyedVector.h
index 42de401..f93ad6e 100644
--- a/include/utils/KeyedVector.h
+++ b/libutils/include/utils/KeyedVector.h
@@ -21,7 +21,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <android/log.h>
+#include <log/log.h>
 #include <utils/Errors.h>
 #include <utils/SortedVector.h>
 #include <utils/TypeHelpers.h>
diff --git a/include/utils/LinearTransform.h b/libutils/include/utils/LinearTransform.h
similarity index 100%
rename from include/utils/LinearTransform.h
rename to libutils/include/utils/LinearTransform.h
diff --git a/include/utils/List.h b/libutils/include/utils/List.h
similarity index 100%
rename from include/utils/List.h
rename to libutils/include/utils/List.h
diff --git a/include/utils/Log.h b/libutils/include/utils/Log.h
similarity index 100%
rename from include/utils/Log.h
rename to libutils/include/utils/Log.h
diff --git a/include/utils/Looper.h b/libutils/include/utils/Looper.h
similarity index 100%
rename from include/utils/Looper.h
rename to libutils/include/utils/Looper.h
diff --git a/include/utils/LruCache.h b/libutils/include/utils/LruCache.h
similarity index 100%
rename from include/utils/LruCache.h
rename to libutils/include/utils/LruCache.h
diff --git a/include/utils/Mutex.h b/libutils/include/utils/Mutex.h
similarity index 100%
rename from include/utils/Mutex.h
rename to libutils/include/utils/Mutex.h
diff --git a/include/utils/NativeHandle.h b/libutils/include/utils/NativeHandle.h
similarity index 94%
rename from include/utils/NativeHandle.h
rename to libutils/include/utils/NativeHandle.h
index b825168..73fe804 100644
--- a/include/utils/NativeHandle.h
+++ b/libutils/include/utils/NativeHandle.h
@@ -24,7 +24,7 @@
 
 namespace android {
 
-class NativeHandle: public LightRefBase<NativeHandle> {
+class NativeHandle : public LightRefBase<NativeHandle> {
 public:
     // Create a refcounted wrapper around a native_handle_t, and declare
     // whether the wrapper owns the handle (so that it should clean up the
@@ -41,7 +41,7 @@
     friend class LightRefBase<NativeHandle>;
 
     NativeHandle(native_handle_t* handle, bool ownsHandle);
-    virtual ~NativeHandle();
+    ~NativeHandle();
 
     native_handle_t* mHandle;
     bool mOwnsHandle;
diff --git a/include/utils/Printer.h b/libutils/include/utils/Printer.h
similarity index 100%
rename from include/utils/Printer.h
rename to libutils/include/utils/Printer.h
diff --git a/include/utils/ProcessCallStack.h b/libutils/include/utils/ProcessCallStack.h
similarity index 100%
rename from include/utils/ProcessCallStack.h
rename to libutils/include/utils/ProcessCallStack.h
diff --git a/include/utils/PropertyMap.h b/libutils/include/utils/PropertyMap.h
similarity index 100%
rename from include/utils/PropertyMap.h
rename to libutils/include/utils/PropertyMap.h
diff --git a/include/utils/RWLock.h b/libutils/include/utils/RWLock.h
similarity index 100%
rename from include/utils/RWLock.h
rename to libutils/include/utils/RWLock.h
diff --git a/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
similarity index 98%
rename from include/utils/RefBase.h
rename to libutils/include/utils/RefBase.h
index 36016cd..a61ea58 100644
--- a/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -371,10 +371,8 @@
 
 private:
     friend class ReferenceMover;
-    inline static void renameRefs(size_t /*n*/,
-            const ReferenceRenamer& /*renamer*/) { }
-    inline static void renameRefId(T* /*ref*/,
-            const void* /*old_id*/ , const void* /*new_id*/) { }
+    inline static void renameRefs(size_t /*n*/, const ReferenceRenamer& /*renamer*/) { }
+    inline static void renameRefId(T* /*ref*/, const void* /*old_id*/ , const void* /*new_id*/) { }
 
 private:
     mutable std::atomic<int32_t> mCount;
diff --git a/include/utils/Singleton.h b/libutils/include/utils/Singleton.h
similarity index 79%
rename from include/utils/Singleton.h
rename to libutils/include/utils/Singleton.h
index 7cc4c18..bdb2332 100644
--- a/include/utils/Singleton.h
+++ b/libutils/include/utils/Singleton.h
@@ -18,14 +18,27 @@
 #define ANDROID_UTILS_SINGLETON_H
 
 #include <stdint.h>
+
+// some vendor code assumes they have atoi() after including this file.
+#include <stdlib.h>
+
 #include <sys/types.h>
 #include <utils/Mutex.h>
-#include <utils/threads.h>
 #include <cutils/compiler.h>
 
 namespace android {
 // ---------------------------------------------------------------------------
 
+// Singleton<TYPE> may be used in multiple libraries, only one of which should
+// define the static member variables using ANDROID_SINGLETON_STATIC_INSTANCE.
+// Turn off -Wundefined-var-template so other users don't get:
+// instantiation of variable 'android::Singleton<TYPE>::sLock' required here,
+// but no definition is available
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundefined-var-template"
+#endif
+
 template <typename TYPE>
 class ANDROID_API Singleton
 {
@@ -56,11 +69,9 @@
     static TYPE* sInstance;
 };
 
-template <typename TYPE>
-Mutex Singleton<TYPE>::sLock;
-
-template <typename TYPE>
-TYPE* Singleton<TYPE>::sInstance;
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
 
 /*
  * use ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) in your implementation file
diff --git a/include/utils/SortedVector.h b/libutils/include/utils/SortedVector.h
similarity index 100%
rename from include/utils/SortedVector.h
rename to libutils/include/utils/SortedVector.h
diff --git a/include/utils/StopWatch.h b/libutils/include/utils/StopWatch.h
similarity index 100%
rename from include/utils/StopWatch.h
rename to libutils/include/utils/StopWatch.h
diff --git a/include/utils/String16.h b/libutils/include/utils/String16.h
similarity index 100%
rename from include/utils/String16.h
rename to libutils/include/utils/String16.h
diff --git a/include/utils/String8.h b/libutils/include/utils/String8.h
similarity index 100%
rename from include/utils/String8.h
rename to libutils/include/utils/String8.h
diff --git a/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
similarity index 98%
rename from include/utils/StrongPointer.h
rename to libutils/include/utils/StrongPointer.h
index 294e6b6..cdfdd8a 100644
--- a/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -17,12 +17,6 @@
 #ifndef ANDROID_STRONG_POINTER_H
 #define ANDROID_STRONG_POINTER_H
 
-#include <cutils/atomic.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <stdlib.h>
-
 // ---------------------------------------------------------------------------
 namespace android {
 
diff --git a/include/utils/SystemClock.h b/libutils/include/utils/SystemClock.h
similarity index 100%
rename from include/utils/SystemClock.h
rename to libutils/include/utils/SystemClock.h
diff --git a/include/utils/Thread.h b/libutils/include/utils/Thread.h
similarity index 100%
rename from include/utils/Thread.h
rename to libutils/include/utils/Thread.h
diff --git a/include/utils/ThreadDefs.h b/libutils/include/utils/ThreadDefs.h
similarity index 100%
rename from include/utils/ThreadDefs.h
rename to libutils/include/utils/ThreadDefs.h
diff --git a/include/utils/Timers.h b/libutils/include/utils/Timers.h
similarity index 100%
rename from include/utils/Timers.h
rename to libutils/include/utils/Timers.h
diff --git a/include/utils/Tokenizer.h b/libutils/include/utils/Tokenizer.h
similarity index 100%
rename from include/utils/Tokenizer.h
rename to libutils/include/utils/Tokenizer.h
diff --git a/include/utils/Trace.h b/libutils/include/utils/Trace.h
similarity index 66%
rename from include/utils/Trace.h
rename to libutils/include/utils/Trace.h
index 6ba68f6..5e9229c 100644
--- a/include/utils/Trace.h
+++ b/libutils/include/utils/Trace.h
@@ -19,24 +19,17 @@
 
 #if defined(__ANDROID__)
 
-#include <fcntl.h>
 #include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
 
-#include <cutils/compiler.h>
-#include <utils/threads.h>
 #include <cutils/trace.h>
 
 // See <cutils/trace.h> for more ATRACE_* macros.
 
-// ATRACE_NAME traces the beginning and end of the current scope.  To trace
-// the correct start and end times this macro should be declared first in the
-// scope body.
-#define ATRACE_NAME(name) android::ScopedTrace ___tracer(ATRACE_TAG, name)
+// ATRACE_NAME traces from its location until the end of its enclosing scope.
+#define _PASTE(x, y) x ## y
+#define PASTE(x, y) _PASTE(x,y)
+#define ATRACE_NAME(name) android::ScopedTrace PASTE(___tracer, __LINE__) (ATRACE_TAG, name)
+
 // ATRACE_CALL is an ATRACE_NAME that uses the current function name.
 #define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
 
@@ -44,14 +37,13 @@
 
 class ScopedTrace {
 public:
-inline ScopedTrace(uint64_t tag, const char* name)
-    : mTag(tag) {
-    atrace_begin(mTag,name);
-}
+    inline ScopedTrace(uint64_t tag, const char* name) : mTag(tag) {
+        atrace_begin(mTag, name);
+    }
 
-inline ~ScopedTrace() {
-    atrace_end(mTag);
-}
+    inline ~ScopedTrace() {
+        atrace_end(mTag);
+    }
 
 private:
     uint64_t mTag;
diff --git a/include/utils/TypeHelpers.h b/libutils/include/utils/TypeHelpers.h
similarity index 100%
rename from include/utils/TypeHelpers.h
rename to libutils/include/utils/TypeHelpers.h
diff --git a/include/utils/Unicode.h b/libutils/include/utils/Unicode.h
similarity index 100%
rename from include/utils/Unicode.h
rename to libutils/include/utils/Unicode.h
diff --git a/include/utils/Vector.h b/libutils/include/utils/Vector.h
similarity index 99%
rename from include/utils/Vector.h
rename to libutils/include/utils/Vector.h
index 28a77b8..9a643f9 100644
--- a/include/utils/Vector.h
+++ b/libutils/include/utils/Vector.h
@@ -20,8 +20,6 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <new>
-
 #include <log/log.h>
 #include <utils/TypeHelpers.h>
 #include <utils/VectorImpl.h>
diff --git a/include/utils/VectorImpl.h b/libutils/include/utils/VectorImpl.h
similarity index 100%
rename from include/utils/VectorImpl.h
rename to libutils/include/utils/VectorImpl.h
diff --git a/include/utils/misc.h b/libutils/include/utils/misc.h
similarity index 100%
rename from include/utils/misc.h
rename to libutils/include/utils/misc.h
diff --git a/include/utils/threads.h b/libutils/include/utils/threads.h
similarity index 100%
rename from include/utils/threads.h
rename to libutils/include/utils/threads.h
diff --git a/libutils/misc.cpp b/libutils/misc.cpp
index 216dc14..2608a65 100644
--- a/libutils/misc.cpp
+++ b/libutils/misc.cpp
@@ -22,12 +22,7 @@
 #include <utils/misc.h>
 #include <utils/Log.h>
 
-#include <sys/stat.h>
-#include <string.h>
-#include <stdio.h>
-
 #if !defined(_WIN32)
-# include <pthread.h>
 #endif
 
 #include <utils/Vector.h>
diff --git a/libutils/primes.py b/libutils/primes.py
deleted file mode 100755
index e161dd8..0000000
--- a/libutils/primes.py
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python2.6
-#
-# 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.
-#
-
-#
-# Generates a table of prime numbers for use in BasicHashtable.cpp.
-#
-# Each prime is chosen such that it is a little more than twice as large as
-# the previous prime in the table.  This makes it easier to choose a new
-# hashtable size when the underlying array is grown by as nominal factor
-# of two each time.
-#
-
-def is_odd_prime(n):
-  limit = (n - 1) / 2
-  d = 3
-  while d <= limit:
-    if n % d == 0:
-      return False
-    d += 2
-  return True
-
-print "static size_t PRIMES[] = {"
-
-n = 5
-max = 2**31 - 1
-while n < max:
-  print "    %d," % (n)
-  n = n * 2 + 1
-  while not is_odd_prime(n):
-    n += 2
-
-print "    0,"
-print "};"
diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp
index ec6b67f..ea606a1 100644
--- a/libutils/tests/Android.bp
+++ b/libutils/tests/Android.bp
@@ -18,33 +18,74 @@
 
 cc_test {
     name: "libutils_tests",
+    host_supported: true,
 
     srcs: [
-        "BlobCache_test.cpp",
         "BitSet_test.cpp",
-        "Looper_test.cpp",
         "LruCache_test.cpp",
-        "RefBase_test.cpp",
+        "Singleton_test.cpp",
         "String8_test.cpp",
         "StrongPointer_test.cpp",
-        "SystemClock_test.cpp",
         "Unicode_test.cpp",
         "Vector_test.cpp",
     ],
 
-    shared_libs: [
-        "libz",
-        "liblog",
-        "libcutils",
-        "libutils",
+    target: {
+        android: {
+            srcs: [
+                "BlobCache_test.cpp",
+                "Looper_test.cpp",
+                "RefBase_test.cpp",
+                "SystemClock_test.cpp",
+            ],
+            shared_libs: [
+                "libz",
+                "liblog",
+                "libcutils",
+                "libutils",
+                "libbase",
+                "libdl",
+            ],
+        },
+        linux: {
+            srcs: [
+                "Looper_test.cpp",
+                "RefBase_test.cpp",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libutils",
+                "liblog",
+                "libbase",
+            ],
+            host_ldlibs: ["-ldl"],
+        },
+    },
+
+    required: [
+        "libutils_tests_singleton1",
+        "libutils_tests_singleton2",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
     ],
 }
 
-cc_test_host {
-    name: "libutils_tests_host",
-    srcs: ["Vector_test.cpp"],
-    static_libs: [
-        "libutils",
-        "liblog",
-    ],
+cc_test_library {
+    name: "libutils_tests_singleton1",
+    host_supported: true,
+    relative_install_path: "libutils_tests",
+    srcs: ["Singleton_test1.cpp"],
+}
+
+cc_test_library {
+    name: "libutils_tests_singleton2",
+    host_supported: true,
+    relative_install_path: "libutils_tests",
+    srcs: ["Singleton_test2.cpp"],
+    shared_libs: ["libutils_tests_singleton1"],
 }
diff --git a/libutils/tests/Singleton_test.cpp b/libutils/tests/Singleton_test.cpp
new file mode 100644
index 0000000..9acd3c3
--- /dev/null
+++ b/libutils/tests/Singleton_test.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Singleton_test"
+
+#include <dlfcn.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <utils/Singleton.h>
+
+#include <gtest/gtest.h>
+
+#include "Singleton_test.h"
+
+namespace android {
+
+TEST(SingletonTest, bug35674422) {
+    std::string path = android::base::GetExecutableDirectory();
+    // libutils_tests_singleton1.so contains the ANDROID_SINGLETON_STATIC_INSTANCE
+    // definition of SingletonTestData, load it first.
+    std::string lib = android::base::StringPrintf("%s/libutils_tests_singleton1.so", path.c_str());
+    void* handle1 = dlopen(lib.c_str(), RTLD_NOW);
+    ASSERT_TRUE(handle1 != nullptr) << dlerror();
+
+    // libutils_tests_singleton2.so references SingletonTestData but should not
+    // have a definition
+    lib = android::base::StringPrintf("%s/libutils_tests_singleton2.so", path.c_str());
+    void* handle2 = dlopen(lib.c_str(), RTLD_NOW);
+    ASSERT_TRUE(handle2 != nullptr) << dlerror();
+
+    using has_fn_t = decltype(&singletonHasInstance);
+    using get_fn_t = decltype(&singletonGetInstanceContents);
+    using set_fn_t = decltype(&singletonSetInstanceContents);
+
+    has_fn_t has1 = reinterpret_cast<has_fn_t>(dlsym(handle1, "singletonHasInstance"));
+    ASSERT_TRUE(has1 != nullptr) << dlerror();
+    has_fn_t has2 = reinterpret_cast<has_fn_t>(dlsym(handle2, "singletonHasInstance"));
+    ASSERT_TRUE(has2 != nullptr) << dlerror();
+    get_fn_t get1 = reinterpret_cast<get_fn_t>(dlsym(handle1, "singletonGetInstanceContents"));
+    ASSERT_TRUE(get1 != nullptr) << dlerror();
+    get_fn_t get2 = reinterpret_cast<get_fn_t>(dlsym(handle2, "singletonGetInstanceContents"));
+    ASSERT_TRUE(get2 != nullptr) << dlerror();
+    set_fn_t set1 = reinterpret_cast<set_fn_t>(dlsym(handle2, "singletonSetInstanceContents"));
+    ASSERT_TRUE(set1 != nullptr) << dlerror();
+
+    EXPECT_FALSE(has1());
+    EXPECT_FALSE(has2());
+    set1(12345678U);
+    EXPECT_TRUE(has1());
+    EXPECT_TRUE(has2());
+    EXPECT_EQ(12345678U, get1());
+    EXPECT_EQ(12345678U, get2());
+}
+
+}
diff --git a/libutils/tests/Singleton_test.h b/libutils/tests/Singleton_test.h
new file mode 100644
index 0000000..c77d9ff
--- /dev/null
+++ b/libutils/tests/Singleton_test.h
@@ -0,0 +1,41 @@
+/*
+ * 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_UTILS_SINGLETON_TEST_H
+#define ANDROID_UTILS_SINGLETON_TEST_H
+
+#include <sys/cdefs.h>
+
+#include "Singleton_test.h"
+
+namespace android {
+
+struct SingletonTestData : Singleton<SingletonTestData> {
+    unsigned int contents;
+};
+
+__BEGIN_DECLS
+
+unsigned int singletonGetInstanceContents();
+void singletonSetInstanceContents(unsigned int);
+bool singletonHasInstance();
+
+__END_DECLS
+
+}
+
+#endif // ANDROID_UTILS_SINGLETON_TEST_H
+
diff --git a/libutils/tests/Singleton_test1.cpp b/libutils/tests/Singleton_test1.cpp
new file mode 100644
index 0000000..4a91ec0
--- /dev/null
+++ b/libutils/tests/Singleton_test1.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 <utils/Singleton.h>
+
+#include "Singleton_test.h"
+
+namespace android {
+
+// Singleton<SingletonTestStruct> is referenced in Singleton_test1.cpp and
+// Singleton_test2.cpp, but only defined in Singleton_test1.cpp.
+ANDROID_SINGLETON_STATIC_INSTANCE(SingletonTestData);
+
+void singletonSetInstanceContents(unsigned int contents) {
+    SingletonTestData::getInstance().contents = contents;
+}
+
+unsigned int singletonGetInstanceContents() {
+    return SingletonTestData::getInstance().contents;
+}
+
+bool singletonHasInstance() {
+    return SingletonTestData::hasInstance();
+}
+
+}
diff --git a/libutils/tests/Singleton_test2.cpp b/libutils/tests/Singleton_test2.cpp
new file mode 100644
index 0000000..eb2a9df
--- /dev/null
+++ b/libutils/tests/Singleton_test2.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 <utils/Singleton.h>
+
+#include "Singleton_test.h"
+
+namespace android {
+
+// Singleton<SingletonTestStruct> is referenced in Singleton_test1.cpp and
+// Singleton_test2.cpp, but only defined in Singleton_test1.cpp.
+
+void singletonSetInstanceContents(unsigned int contents) {
+    SingletonTestData::getInstance().contents = contents;
+}
+
+unsigned int singletonGetInstanceContents() {
+    return SingletonTestData::getInstance().contents;
+}
+
+bool singletonHasInstance() {
+    return SingletonTestData::hasInstance();
+}
+
+}
diff --git a/libutils/tests/StrongPointer_test.cpp b/libutils/tests/StrongPointer_test.cpp
index 323a6f2..153cf96 100644
--- a/libutils/tests/StrongPointer_test.cpp
+++ b/libutils/tests/StrongPointer_test.cpp
@@ -21,13 +21,13 @@
 
 using namespace android;
 
-class Foo : public LightRefBase<Foo> {
+class SPFoo : public LightRefBase<SPFoo> {
 public:
-    explicit Foo(bool* deleted_check) : mDeleted(deleted_check) {
+    explicit SPFoo(bool* deleted_check) : mDeleted(deleted_check) {
         *mDeleted = false;
     }
 
-    ~Foo() {
+    ~SPFoo() {
         *mDeleted = true;
     }
 private:
@@ -36,13 +36,13 @@
 
 TEST(StrongPointer, move) {
     bool isDeleted;
-    Foo* foo = new Foo(&isDeleted);
+    SPFoo* foo = new SPFoo(&isDeleted);
     ASSERT_EQ(0, foo->getStrongCount());
     ASSERT_FALSE(isDeleted) << "Already deleted...?";
-    sp<Foo> sp1(foo);
+    sp<SPFoo> sp1(foo);
     ASSERT_EQ(1, foo->getStrongCount());
     {
-        sp<Foo> sp2 = std::move(sp1);
+        sp<SPFoo> sp2 = std::move(sp1);
         ASSERT_EQ(1, foo->getStrongCount()) << "std::move failed, incremented refcnt";
         ASSERT_EQ(nullptr, sp1.get()) << "std::move failed, sp1 is still valid";
         // The strong count isn't increasing, let's double check the old object
@@ -52,7 +52,7 @@
     ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
     {
         // Now let's double check it deletes on time
-        sp<Foo> sp2 = std::move(sp1);
+        sp<SPFoo> sp2 = std::move(sp1);
     }
     ASSERT_TRUE(isDeleted) << "foo was leaked!";
 }
diff --git a/libutils/tests/Vector_test.cpp b/libutils/tests/Vector_test.cpp
index 671200f..e074a92 100644
--- a/libutils/tests/Vector_test.cpp
+++ b/libutils/tests/Vector_test.cpp
@@ -74,12 +74,9 @@
     EXPECT_EQ(other[3], 5);
 }
 
-// TODO: gtest isn't capable of parsing Abort messages formatted by
-// Android (fails differently on host and target), so we always need to
-// use an empty error message for death tests.
 TEST_F(VectorTest, SetCapacity_Overflow) {
   Vector<int> vector;
-  EXPECT_DEATH(vector.setCapacity(SIZE_MAX / sizeof(int) + 1), "");
+  EXPECT_DEATH(vector.setCapacity(SIZE_MAX / sizeof(int) + 1), "Assertion failed");
 }
 
 TEST_F(VectorTest, SetCapacity_ShrinkBelowSize) {
@@ -95,20 +92,13 @@
   ASSERT_EQ(8U, vector.capacity());
 }
 
-// NOTE: All of the tests below are useless because of the "TODO" above.
-// We have no way of knowing *why* the process crashed. Given that we're
-// inserting a NULL array, we'll fail with a SIGSEGV eventually. We need
-// the ability to make assertions on the abort message to make sure we're
-// failing for the right reasons.
 TEST_F(VectorTest, _grow_OverflowSize) {
   Vector<int> vector;
   vector.add(1);
 
   // Checks that the size calculation (not the capacity calculation) doesn't
   // overflow : the size here will be (1 + SIZE_MAX).
-  //
-  // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), "new_size_overflow");
-  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), "");
+  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), "new_size overflow");
 }
 
 TEST_F(VectorTest, _grow_OverflowCapacityDoubling) {
@@ -116,18 +106,14 @@
 
   // This should fail because the calculated capacity will overflow even though
   // the size of the vector doesn't.
-  //
-  // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), "new_capacity_overflow");
-  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), "");
+  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), "new_capacity overflow");
 }
 
 TEST_F(VectorTest, _grow_OverflowBufferAlloc) {
   Vector<int> vector;
   // This should fail because the capacity * sizeof(int) overflows, even
   // though the capacity itself doesn't.
-  //
-  // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), "new_alloc_size overflow");
-  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), "");
+  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), "new_alloc_size overflow");
 }
 
 TEST_F(VectorTest, editArray_Shared) {
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index fce1378..44daf36 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -20,6 +20,7 @@
         "-DZLIB_CONST",
         "-Werror",
         "-Wall",
+        "-D_FILE_OFFSET_BITS=64",
     ],
     cppflags: [
         "-Wold-style-cast",
@@ -56,11 +57,13 @@
     name: "libziparchive",
     host_supported: true,
     defaults: ["libziparchive_defaults", "libziparchive_flags"],
-    static_libs: ["libutils"],
     shared_libs: ["liblog", "libbase"],
     target: {
         android: {
-            static_libs: ["libz"],
+            shared_libs: ["libz", "libutils"],
+        },
+        host: {
+            static_libs: ["libutils"],
         },
         linux_bionic: {
             static_libs: ["libz"],
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
index 64b24c3..3f336a6 100644
--- a/libziparchive/zip_archive_stream_entry.cc
+++ b/libziparchive/zip_archive_stream_entry.cc
@@ -26,8 +26,9 @@
 #include <memory>
 #include <vector>
 
-#include <android/log.h>
 #include <android-base/file.h>
+#include <log/log.h>
+
 #include <ziparchive/zip_archive.h>
 #include <ziparchive/zip_archive_stream_entry.h>
 #include <zlib.h>
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 49746b3..8a6168c 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -31,8 +31,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <android/log.h>
 #include <cutils/sockets.h>
+#include <log/log.h>
 #include <processgroup/processgroup.h>
 
 #ifndef __unused
diff --git a/logcat/.clang-format b/logcat/.clang-format
new file mode 100644
index 0000000..393c309
--- /dev/null
+++ b/logcat/.clang-format
@@ -0,0 +1,11 @@
+BasedOnStyle: Google
+AllowShortFunctionsOnASingleLine: false
+
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+PenaltyExcessCharacter: 32
+
+Cpp11BracedListStyle: false
diff --git a/logcat/Android.mk b/logcat/Android.mk
index 1dacbe1..f564f0f 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -1,20 +1,31 @@
 # Copyright 2006-2014 The Android Open Source Project
 
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
+
+logcatLibs := liblog libbase libcutils libpcrecpp
+
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:= logcat.cpp event.logtags
-
-LOCAL_SHARED_LIBRARIES := liblog libbase libcutils libpcrecpp
-
 LOCAL_MODULE := logcat
-
+LOCAL_SRC_FILES := logcat_main.cpp event.logtags
+LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
 LOCAL_CFLAGS := -Werror
 
 include $(BUILD_EXECUTABLE)
 
 include $(CLEAR_VARS)
 
+LOCAL_MODULE := liblogcat
+LOCAL_SRC_FILES := logcat.cpp getopt_long.cpp logcat_system.cpp
+LOCAL_SHARED_LIBRARIES := $(logcatLibs)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Werror
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
 LOCAL_MODULE := logpersist.start
 LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE_CLASS := EXECUTABLES
diff --git a/logcat/getopt_long.cpp b/logcat/getopt_long.cpp
new file mode 100644
index 0000000..5f8dd66
--- /dev/null
+++ b/logcat/getopt_long.cpp
@@ -0,0 +1,402 @@
+/* $OpenBSD: getopt_long.c,v 1.26 2013/06/08 22:47:56 millert Exp $ */
+/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $       */
+
+/*
+ * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+
+#include <log/getopt.h>
+
+#define PRINT_ERROR ((context->opterr) && (*options != ':'))
+
+#define FLAG_PERMUTE 0x01  // permute non-options to the end of argv
+#define FLAG_ALLARGS 0x02  // treat non-options as args to option "-1"
+
+// return values
+#define BADCH (int)'?'
+#define BADARG ((*options == ':') ? (int)':' : (int)'?')
+#define INORDER (int)1
+
+#define D_PREFIX 0
+#define DD_PREFIX 1
+#define W_PREFIX 2
+
+// Compute the greatest common divisor of a and b.
+static int gcd(int a, int b) {
+    int c = a % b;
+    while (c) {
+        a = b;
+        b = c;
+        c = a % b;
+    }
+    return b;
+}
+
+// Exchange the block from nonopt_start to nonopt_end with the block from
+// nonopt_end to opt_end (keeping the same order of arguments in each block).
+// Returns optind - (nonopt_end - nonopt_start) for convenience.
+static int permute_args(getopt_context* context, char* const* nargv) {
+    // compute lengths of blocks and number and size of cycles
+    int nnonopts = context->nonopt_end - context->nonopt_start;
+    int nopts = context->optind - context->nonopt_end;
+    int ncycle = gcd(nnonopts, nopts);
+    int cyclelen = (context->optind - context->nonopt_start) / ncycle;
+
+    for (int i = 0; i < ncycle; i++) {
+        int cstart = context->nonopt_end + i;
+        int pos = cstart;
+        for (int j = 0; j < cyclelen; j++) {
+            if (pos >= context->nonopt_end) {
+                pos -= nnonopts;
+            } else {
+                pos += nopts;
+            }
+            char* swap = nargv[pos];
+            const_cast<char**>(nargv)[pos] = nargv[cstart];
+            const_cast<char**>(nargv)[cstart] = swap;
+        }
+    }
+    return context->optind - (context->nonopt_end - context->nonopt_start);
+}
+
+// parse_long_options_r --
+//    Parse long options in argc/argv argument vector.
+// Returns -1 if short_too is set and the option does not match long_options.
+static int parse_long_options_r(char* const* nargv, const char* options,
+                                const struct option* long_options, int* idx,
+                                bool short_too, struct getopt_context* context) {
+    const char* current_argv = context->place;
+    const char* current_dash;
+    switch (context->dash_prefix) {
+        case D_PREFIX:
+            current_dash = "-";
+            break;
+        case DD_PREFIX:
+            current_dash = "--";
+            break;
+        case W_PREFIX:
+            current_dash = "-W ";
+            break;
+        default:
+            current_dash = "";
+            break;
+    }
+    context->optind++;
+
+    const char* has_equal;
+    size_t current_argv_len;
+    if (!!(has_equal = strchr(current_argv, '='))) {
+        // argument found (--option=arg)
+        current_argv_len = has_equal - current_argv;
+        has_equal++;
+    } else {
+        current_argv_len = strlen(current_argv);
+    }
+
+    int match = -1;
+    bool exact_match = false;
+    bool second_partial_match = false;
+    for (int i = 0; long_options[i].name; i++) {
+        // find matching long option
+        if (strncmp(current_argv, long_options[i].name, current_argv_len)) {
+            continue;
+        }
+
+        if (strlen(long_options[i].name) == current_argv_len) {
+            // exact match
+            match = i;
+            exact_match = true;
+            break;
+        }
+        // If this is a known short option, don't allow
+        // a partial match of a single character.
+        if (short_too && current_argv_len == 1) continue;
+
+        if (match == -1) {  // first partial match
+            match = i;
+        } else if (long_options[i].has_arg != long_options[match].has_arg ||
+                   long_options[i].flag != long_options[match].flag ||
+                   long_options[i].val != long_options[match].val) {
+            second_partial_match = true;
+        }
+    }
+    if (!exact_match && second_partial_match) {
+        // ambiguous abbreviation
+        if (PRINT_ERROR) {
+            fprintf(context->optstderr ?: stderr,
+                    "option `%s%.*s' is ambiguous", current_dash,
+                    (int)current_argv_len, current_argv);
+        }
+        context->optopt = 0;
+        return BADCH;
+    }
+    if (match != -1) {  // option found
+        if (long_options[match].has_arg == no_argument && has_equal) {
+            if (PRINT_ERROR) {
+                fprintf(context->optstderr ?: stderr,
+                        "option `%s%.*s' doesn't allow an argument",
+                        current_dash, (int)current_argv_len, current_argv);
+            }
+            // XXX: GNU sets optopt to val regardless of flag
+            context->optopt =
+                long_options[match].flag ? 0 : long_options[match].val;
+            return BADCH;
+        }
+        if (long_options[match].has_arg == required_argument ||
+            long_options[match].has_arg == optional_argument) {
+            if (has_equal) {
+                context->optarg = has_equal;
+            } else if (long_options[match].has_arg == required_argument) {
+                // optional argument doesn't use next nargv
+                context->optarg = nargv[context->optind++];
+            }
+        }
+        if ((long_options[match].has_arg == required_argument) &&
+            !context->optarg) {
+            // Missing argument; leading ':' indicates no error
+            // should be generated.
+            if (PRINT_ERROR) {
+                fprintf(context->optstderr ?: stderr,
+                        "option `%s%s' requires an argument", current_dash,
+                        current_argv);
+            }
+            // XXX: GNU sets optopt to val regardless of flag
+            context->optopt =
+                long_options[match].flag ? 0 : long_options[match].val;
+            context->optind--;
+            return BADARG;
+        }
+    } else {  // unknown option
+        if (short_too) {
+            context->optind--;
+            return -1;
+        }
+        if (PRINT_ERROR) {
+            fprintf(context->optstderr ?: stderr, "unrecognized option `%s%s'",
+                    current_dash, current_argv);
+        }
+        context->optopt = 0;
+        return BADCH;
+    }
+    if (idx) *idx = match;
+    if (long_options[match].flag) {
+        *long_options[match].flag = long_options[match].val;
+        return 0;
+    }
+    return long_options[match].val;
+}
+
+// getopt_long_r --
+//    Parse argc/argv argument vector.
+int getopt_long_r(int nargc, char* const* nargv, const char* options,
+                  const struct option* long_options, int* idx,
+                  struct getopt_context* context) {
+    if (!options) return -1;
+
+    // XXX Some GNU programs (like cvs) set optind to 0 instead of
+    // XXX using optreset.  Work around this braindamage.
+    if (!context->optind) context->optind = context->optreset = 1;
+
+    // Disable GNU extensions if options string begins with a '+'.
+    int flags = FLAG_PERMUTE;
+    if (*options == '-') {
+        flags |= FLAG_ALLARGS;
+    } else if (*options == '+') {
+        flags &= ~FLAG_PERMUTE;
+    }
+    if (*options == '+' || *options == '-') options++;
+
+    context->optarg = nullptr;
+    if (context->optreset) context->nonopt_start = context->nonopt_end = -1;
+start:
+    if (context->optreset || !*context->place) {  // update scanning pointer
+        context->optreset = 0;
+        if (context->optind >= nargc) {  // end of argument vector
+            context->place = EMSG;
+            if (context->nonopt_end != -1) {
+                // do permutation, if we have to
+                context->optind = permute_args(context, nargv);
+            } else if (context->nonopt_start != -1) {
+                // If we skipped non-options, set optind to the first of them.
+                context->optind = context->nonopt_start;
+            }
+            context->nonopt_start = context->nonopt_end = -1;
+            return -1;
+        }
+        if (*(context->place = nargv[context->optind]) != '-' ||
+            context->place[1] == '\0') {
+            context->place = EMSG;  // found non-option
+            if (flags & FLAG_ALLARGS) {
+                // GNU extension: return non-option as argument to option 1
+                context->optarg = nargv[context->optind++];
+                return INORDER;
+            }
+            if (!(flags & FLAG_PERMUTE)) {
+                // If no permutation wanted, stop parsing at first non-option.
+                return -1;
+            }
+            // do permutation
+            if (context->nonopt_start == -1) {
+                context->nonopt_start = context->optind;
+            } else if (context->nonopt_end != -1) {
+                context->nonopt_start = permute_args(context, nargv);
+                context->nonopt_end = -1;
+            }
+            context->optind++;
+            // process next argument
+            goto start;
+        }
+        if (context->nonopt_start != -1 && context->nonopt_end == -1) {
+            context->nonopt_end = context->optind;
+        }
+
+        // If we have "-" do nothing, if "--" we are done.
+        if (context->place[1] != '\0' && *++(context->place) == '-' &&
+            context->place[1] == '\0') {
+            context->optind++;
+            context->place = EMSG;
+            // We found an option (--), so if we skipped
+            // non-options, we have to permute.
+            if (context->nonopt_end != -1) {
+                context->optind = permute_args(context, nargv);
+            }
+            context->nonopt_start = context->nonopt_end = -1;
+            return -1;
+        }
+    }
+
+    int optchar;
+    // Check long options if:
+    //  1) we were passed some
+    //  2) the arg is not just "-"
+    //  3) either the arg starts with -- we are getopt_long_only()
+    if (long_options && context->place != nargv[context->optind] &&
+        (*context->place == '-')) {
+        bool short_too = false;
+        context->dash_prefix = D_PREFIX;
+        if (*context->place == '-') {
+            context->place++;  // --foo long option
+            context->dash_prefix = DD_PREFIX;
+        } else if (*context->place != ':' && strchr(options, *context->place)) {
+            short_too = true;  // could be short option too
+        }
+
+        optchar = parse_long_options_r(nargv, options, long_options, idx,
+                                       short_too, context);
+        if (optchar != -1) {
+            context->place = EMSG;
+            return optchar;
+        }
+    }
+
+    const char* oli;  // option letter list index
+    if ((optchar = (int)*(context->place)++) == (int)':' ||
+        (optchar == (int)'-' && *context->place != '\0') ||
+        !(oli = strchr(options, optchar))) {
+        // If the user specified "-" and  '-' isn't listed in
+        // options, return -1 (non-option) as per POSIX.
+        // Otherwise, it is an unknown option character (or ':').
+        if (optchar == (int)'-' && *context->place == '\0') return -1;
+        if (!*context->place) context->optind++;
+        if (PRINT_ERROR) {
+            fprintf(context->optstderr ?: stderr, "invalid option -- %c",
+                    optchar);
+        }
+        context->optopt = optchar;
+        return BADCH;
+    }
+
+    static const char recargchar[] = "option requires an argument -- %c";
+    if (long_options && optchar == 'W' && oli[1] == ';') {
+        // -W long-option
+        if (*context->place) {                      // no space
+            ;                                       // NOTHING
+        } else if (++(context->optind) >= nargc) {  // no arg
+            context->place = EMSG;
+            if (PRINT_ERROR) {
+                fprintf(context->optstderr ?: stderr, recargchar, optchar);
+            }
+            context->optopt = optchar;
+            return BADARG;
+        } else {  // white space
+            context->place = nargv[context->optind];
+        }
+        context->dash_prefix = W_PREFIX;
+        optchar = parse_long_options_r(nargv, options, long_options, idx, false,
+                                       context);
+        context->place = EMSG;
+        return optchar;
+    }
+    if (*++oli != ':') {  // doesn't take argument
+        if (!*context->place) context->optind++;
+    } else {  // takes (optional) argument
+        context->optarg = nullptr;
+        if (*context->place) {  // no white space
+            context->optarg = context->place;
+        } else if (oli[1] != ':') {              // arg not optional
+            if (++(context->optind) >= nargc) {  // no arg
+                context->place = EMSG;
+                if (PRINT_ERROR) {
+                    fprintf(context->optstderr ?: stderr, recargchar, optchar);
+                }
+                context->optopt = optchar;
+                return BADARG;
+            }
+            context->optarg = nargv[context->optind];
+        }
+        context->place = EMSG;
+        context->optind++;
+    }
+    // dump back option letter
+    return optchar;
+}
diff --git a/logcat/include/log/getopt.h b/logcat/include/log/getopt.h
new file mode 100644
index 0000000..0da2b10
--- /dev/null
+++ b/logcat/include/log/getopt.h
@@ -0,0 +1,67 @@
+/*
+ * 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 _LOG_GETOPT_H_
+#define _LOG_GETOPT_H_
+
+#ifndef __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
+#elif __ANDROID_API__ > 24 /* > Nougat */
+#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
+
+#include <getopt.h>
+#include <sys/cdefs.h>
+
+struct getopt_context {
+    int opterr;
+    int optind;
+    int optopt;
+    int optreset;
+    const char* optarg;
+    FILE* optstderr; /* NULL defaults to stderr */
+    /* private */
+    const char* place;
+    int nonopt_start;
+    int nonopt_end;
+    int dash_prefix;
+    /* expansion space */
+    int __extra__;
+    void* __stuff__;
+};
+
+#define EMSG ""
+#define NO_PREFIX (-1)
+
+#define INIT_GETOPT_CONTEXT(context) \
+    context = { 1, 1, '?', 0, NULL, NULL, EMSG, -1, -1, NO_PREFIX, 0, NULL }
+
+__BEGIN_DECLS
+int getopt_long_r(int nargc, char* const* nargv, const char* options,
+                  const struct option* long_options, int* idx,
+                  struct getopt_context* context);
+
+__END_DECLS
+
+#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
+
+#endif /* !_LOG_GETOPT_H_ */
diff --git a/logcat/include/log/logcat.h b/logcat/include/log/logcat.h
new file mode 100644
index 0000000..009672c
--- /dev/null
+++ b/logcat/include/log/logcat.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2005-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 _LIBS_LOGCAT_H /* header boilerplate */
+#define _LIBS_LOGCAT_H
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
+#elif __ANDROID_API__ > 24 /* > Nougat */
+#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
+
+/* For managing an in-process logcat function, rather than forking/execing
+ *
+ * It also serves as the basis for the logcat command.
+ *
+ * The following C API allows a logcat instance to be created, run
+ * to completion, and then release all the associated resources.
+ */
+
+/*
+ * The opaque context
+ */
+#ifndef __android_logcat_context_defined /* typedef boilerplate */
+#define __android_logcat_context_defined
+typedef struct android_logcat_context_internal* android_logcat_context;
+#endif
+
+/* Creates a context associated with this logcat instance
+ *
+ * Returns a pointer to the context, or a NULL on error.
+ */
+android_logcat_context create_android_logcat();
+
+/* Collects and outputs the logcat data to output and error file descriptors
+ *
+ * Will block, performed in-thread and in-process
+ *
+ * The output file descriptor variable, if greater than or equal to 0, is
+ * where the output (ie: stdout) will be sent. The file descriptor is closed
+ * on android_logcat_destroy which terminates the instance, or when an -f flag
+ * (output redirect to a file) is present in the command.  The error file
+ * descriptor variable, if greater than or equal to 0, is where the error
+ * stream (ie: stderr) will be sent, also closed on android_logcat_destroy.
+ * The error file descriptor can be set to equal to the output file descriptor,
+ * which will mix output and error stream content, and will defer closure of
+ * the file descriptor on -f flag redirection.  Negative values for the file
+ * descriptors will use stdout and stderr FILE references respectively
+ * internally, and will not close the references as noted above.
+ *
+ * Return value is 0 for success, non-zero for errors.
+ */
+int android_logcat_run_command(android_logcat_context ctx, int output, int error,
+                               int argc, char* const* argv, char* const* envp);
+
+/* Will not block, performed in-process
+ *
+ * Starts a thread, opens a pipe, returns reading end fd, saves off argv.
+ * The command supports 2>&1 (mix content) and 2>/dev/null (drop content) for
+ * scripted error (stderr) redirection.
+ */
+int android_logcat_run_command_thread(android_logcat_context ctx, int argc,
+                                      char* const* argv, char* const* envp);
+int android_logcat_run_command_thread_running(android_logcat_context ctx);
+
+/* Finished with context
+ *
+ * Kill the command thread ASAP (if any), and free up all associated resources.
+ *
+ * Return value is the result of the android_logcat_run_command, or
+ * non-zero for any errors.
+ */
+int android_logcat_destroy(android_logcat_context* ctx);
+
+/* derived helpers */
+
+/*
+ * In-process thread that acts like somewhat like libc-like system and popen
+ * respectively.  Can not handle shell scripting, only pure calls to the
+ * logcat operations. The android_logcat_system is a wrapper for the
+ * create_android_logcat, android_logcat_run_command and android_logcat_destroy
+ * API above.  The android_logcat_popen is a wrapper for the
+ * android_logcat_run_command_thread API above.  The android_logcat_pclose is
+ * a wrapper for a reasonable wait until output has subsided for command
+ * completion, fclose on the FILE pointer and the android_logcat_destroy API.
+ */
+int android_logcat_system(const char* command);
+FILE* android_logcat_popen(android_logcat_context* ctx, const char* command);
+int android_logcat_pclose(android_logcat_context* ctx, FILE* output);
+
+#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOGCAT_H */
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index c204a16..3cd36f9 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -1,4 +1,18 @@
-// Copyright 2006-2015 The Android Open Source Project
+/*
+ * Copyright (C) 2006-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 <arpa/inet.h>
 #include <assert.h>
@@ -6,10 +20,9 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <getopt.h>
 #include <math.h>
+#include <pthread.h>
 #include <sched.h>
-#include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -22,8 +35,10 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <atomic>
 #include <memory>
 #include <string>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
@@ -31,6 +46,8 @@
 #include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
+#include <log/getopt.h>
+#include <log/logcat.h>
 #include <log/logprint.h>
 #include <private/android_logger.h>
 #include <system/thread_defs.h>
@@ -39,16 +56,82 @@
 
 #define DEFAULT_MAX_ROTATED_LOGS 4
 
-static AndroidLogFormat * g_logformat;
+struct android_logcat_context_internal {
+    // status
+    volatile std::atomic_int retval;  // valid if thread_stopped set
+    // Arguments passed in, or copies and storage thereof if a thread.
+    int argc;
+    char* const* argv;
+    char* const* envp;
+    std::vector<std::string> args;
+    std::vector<const char*> argv_hold;
+    std::vector<std::string> envs;
+    std::vector<const char*> envp_hold;
+    int output_fd;  // duplication of fileno(output) (below)
+    int error_fd;   // duplication of fileno(error) (below)
 
-/* logd prefixes records with a length field */
+    // library
+    int fds[2];    // From popen call
+    FILE* output;  // everything writes to fileno(output), buffer unused
+    FILE* error;   // unless error == output.
+    pthread_t thr;
+    volatile std::atomic_bool stop;  // quick exit flag
+    volatile std::atomic_bool thread_stopped;
+    bool stderr_null;    // shell "2>/dev/null"
+    bool stderr_stdout;  // shell "2>&1"
+
+    // global variables
+    AndroidLogFormat* logformat;
+    const char* outputFileName;
+    // 0 means "no log rotation"
+    size_t logRotateSizeKBytes;
+    // 0 means "unbounded"
+    size_t maxRotatedLogs;
+    size_t outByteCount;
+    int printBinary;
+    int devCount;  // >1 means multiple
+    pcrecpp::RE* regex;
+    // 0 means "infinite"
+    size_t maxCount;
+    size_t printCount;
+    bool printItAnyways;
+    bool debug;
+
+    // static variables
+    bool hasOpenedEventTagMap;
+    EventTagMap* eventTagMap;
+};
+
+// Creates a context associated with this logcat instance
+android_logcat_context create_android_logcat() {
+    android_logcat_context_internal* context;
+
+    context = (android_logcat_context_internal*)calloc(
+        1, sizeof(android_logcat_context_internal));
+    if (!context) return nullptr;
+
+    context->fds[0] = -1;
+    context->fds[1] = -1;
+    context->output_fd = -1;
+    context->error_fd = -1;
+    context->maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
+
+    context->argv_hold.clear();
+    context->args.clear();
+    context->envp_hold.clear();
+    context->envs.clear();
+
+    return (android_logcat_context)context;
+}
+
+// logd prefixes records with a length field
 #define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
 
 struct log_device_t {
     const char* device;
     bool binary;
-    struct logger *logger;
-    struct logger_list *logger_list;
+    struct logger* logger;
+    struct logger_list* logger_list;
     bool printed;
 
     log_device_t* next;
@@ -56,76 +139,129 @@
     log_device_t(const char* d, bool b) {
         device = d;
         binary = b;
-        next = NULL;
+        next = nullptr;
         printed = false;
-        logger = NULL;
-        logger_list = NULL;
+        logger = nullptr;
+        logger_list = nullptr;
     }
 };
 
 namespace android {
 
-/* Global Variables */
-
-static const char * g_outputFileName;
-// 0 means "no log rotation"
-static size_t g_logRotateSizeKBytes;
-// 0 means "unbounded"
-static size_t g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
-static int g_outFD = -1;
-static size_t g_outByteCount;
-static int g_printBinary;
-static int g_devCount;                              // >1 means multiple
-static pcrecpp::RE* g_regex;
-// 0 means "infinite"
-static size_t g_maxCount;
-static size_t g_printCount;
-static bool g_printItAnyways;
-static bool g_debug;
-
-enum helpType {
-    HELP_FALSE,
-    HELP_TRUE,
-    HELP_FORMAT
-};
+enum helpType { HELP_FALSE, HELP_TRUE, HELP_FORMAT };
 
 // if showHelp is set, newline required in fmt statement to transition to usage
-__noreturn static void logcat_panic(enum helpType showHelp, const char *fmt, ...) __printflike(2,3);
+static void logcat_panic(android_logcat_context_internal* context,
+                         enum helpType showHelp, const char* fmt, ...)
+    __printflike(3, 4);
 
-static int openLogFile (const char *pathname)
-{
+static int openLogFile(const char* pathname) {
     return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
 }
 
-static void rotateLogs()
-{
+static void close_output(android_logcat_context_internal* context) {
+    // split output_from_error
+    if (context->error == context->output) {
+        context->output = nullptr;
+        context->output_fd = -1;
+    }
+    if (context->error && (context->output_fd == fileno(context->error))) {
+        context->output_fd = -1;
+    }
+    if (context->output_fd == context->error_fd) {
+        context->output_fd = -1;
+    }
+    // close output channel
+    if (context->output) {
+        if (context->output != stdout) {
+            if (context->output_fd == fileno(context->output)) {
+                context->output_fd = -1;
+            }
+            if (context->fds[1] == fileno(context->output)) {
+                context->fds[1] = -1;
+            }
+            fclose(context->output);
+        }
+        context->output = nullptr;
+    }
+    if (context->output_fd >= 0) {
+        if (context->output_fd != fileno(stdout)) {
+            if (context->fds[1] == context->output_fd) {
+                context->fds[1] = -1;
+            }
+            close(context->output_fd);
+        }
+        context->output_fd = -1;
+    }
+}
+
+static void close_error(android_logcat_context_internal* context) {
+    // split error_from_output
+    if (context->output == context->error) {
+        context->error = nullptr;
+        context->error_fd = -1;
+    }
+    if (context->output && (context->error_fd == fileno(context->output))) {
+        context->error_fd = -1;
+    }
+    if (context->error_fd == context->output_fd) {
+        context->error_fd = -1;
+    }
+    // close error channel
+    if (context->error) {
+        if ((context->error != stderr) && (context->error != stdout)) {
+            if (context->error_fd == fileno(context->error)) {
+                context->error_fd = -1;
+            }
+            if (context->fds[1] == fileno(context->error)) {
+                context->fds[1] = -1;
+            }
+            fclose(context->error);
+        }
+        context->error = nullptr;
+    }
+    if (context->error_fd >= 0) {
+        if ((context->error_fd != fileno(stdout)) &&
+            (context->error_fd != fileno(stderr))) {
+            if (context->fds[1] == context->error_fd) context->fds[1] = -1;
+            close(context->error_fd);
+        }
+        context->error_fd = -1;
+    }
+}
+
+static void rotateLogs(android_logcat_context_internal* context) {
     int err;
 
     // Can't rotate logs if we're not outputting to a file
-    if (g_outputFileName == NULL) {
-        return;
-    }
+    if (!context->outputFileName) return;
 
-    close(g_outFD);
+    close_output(context);
 
-    // Compute the maximum number of digits needed to count up to g_maxRotatedLogs in decimal.
-    // eg: g_maxRotatedLogs == 30 -> log10(30) == 1.477 -> maxRotationCountDigits == 2
+    // Compute the maximum number of digits needed to count up to
+    // maxRotatedLogs in decimal.  eg:
+    // maxRotatedLogs == 30
+    //   -> log10(30) == 1.477
+    //   -> maxRotationCountDigits == 2
     int maxRotationCountDigits =
-            (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
+        (context->maxRotatedLogs > 0)
+            ? (int)(floor(log10(context->maxRotatedLogs) + 1))
+            : 0;
 
-    for (int i = g_maxRotatedLogs ; i > 0 ; i--) {
+    for (int i = context->maxRotatedLogs; i > 0; i--) {
         std::string file1 = android::base::StringPrintf(
-            "%s.%.*d", g_outputFileName, maxRotationCountDigits, i);
+            "%s.%.*d", context->outputFileName, maxRotationCountDigits, i);
 
         std::string file0;
-        if (i - 1 == 0) {
-            file0 = android::base::StringPrintf("%s", g_outputFileName);
+        if (!(i - 1)) {
+            file0 = android::base::StringPrintf("%s", context->outputFileName);
         } else {
-            file0 = android::base::StringPrintf(
-                "%s.%.*d", g_outputFileName, maxRotationCountDigits, i - 1);
+            file0 =
+                android::base::StringPrintf("%s.%.*d", context->outputFileName,
+                                            maxRotationCountDigits, i - 1);
         }
 
-        if ((file0.length() == 0) || (file1.length() == 0)) {
+        if (!file0.length() || !file1.length()) {
             perror("while rotating log files");
             break;
         }
@@ -137,153 +273,165 @@
         }
     }
 
-    g_outFD = openLogFile(g_outputFileName);
+    context->output_fd = openLogFile(context->outputFileName);
 
-    if (g_outFD < 0) {
-        logcat_panic(HELP_FALSE, "couldn't open output file");
+    if (context->output_fd < 0) {
+        logcat_panic(context, HELP_FALSE, "couldn't open output file");
+        return;
+    }
+    context->output = fdopen(context->output_fd, "web");
+    if (!context->output) {
+        logcat_panic(context, HELP_FALSE, "couldn't fdopen output file");
+        return;
+    }
+    if (context->stderr_stdout) {
+        close_error(context);
+        context->error = context->output;
+        context->error_fd = context->output_fd;
     }
 
-    g_outByteCount = 0;
-
+    context->outByteCount = 0;
 }
 
-void printBinary(struct log_msg *buf)
-{
+void printBinary(android_logcat_context_internal* context, struct log_msg* buf) {
     size_t size = buf->len();
 
-    TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
+    TEMP_FAILURE_RETRY(write(context->output_fd, buf, size));
 }
 
-static bool regexOk(const AndroidLogEntry& entry)
-{
-    if (!g_regex) {
-        return true;
-    }
+static bool regexOk(android_logcat_context_internal* context,
+                    const AndroidLogEntry& entry) {
+    if (!context->regex) return true;
 
     std::string messageString(entry.message, entry.messageLen);
 
-    return g_regex->PartialMatch(messageString);
+    return context->regex->PartialMatch(messageString);
 }
 
-static void processBuffer(log_device_t* dev, struct log_msg *buf)
-{
+static void processBuffer(android_logcat_context_internal* context,
+                          log_device_t* dev, struct log_msg* buf) {
     int bytesWritten = 0;
     int err;
     AndroidLogEntry entry;
     char binaryMsgBuf[1024];
 
     if (dev->binary) {
-        static bool hasOpenedEventTagMap = false;
-        static EventTagMap *eventTagMap = NULL;
-
-        if (!eventTagMap && !hasOpenedEventTagMap) {
-            eventTagMap = android_openEventTagMap(NULL);
-            hasOpenedEventTagMap = true;
+        if (!context->eventTagMap && !context->hasOpenedEventTagMap) {
+            context->eventTagMap = android_openEventTagMap(nullptr);
+            context->hasOpenedEventTagMap = true;
         }
-        err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry,
-                                                 eventTagMap,
-                                                 binaryMsgBuf,
-                                                 sizeof(binaryMsgBuf));
-        //printf(">>> pri=%d len=%d msg='%s'\n",
+        err = android_log_processBinaryLogBuffer(
+            &buf->entry_v1, &entry, context->eventTagMap, binaryMsgBuf,
+            sizeof(binaryMsgBuf));
+        // printf(">>> pri=%d len=%d msg='%s'\n",
         //    entry.priority, entry.messageLen, entry.message);
     } else {
         err = android_log_processLogBuffer(&buf->entry_v1, &entry);
     }
-    if ((err < 0) && !g_debug) {
-        goto error;
-    }
+    if ((err < 0) && !context->debug) return;
 
-    if (android_log_shouldPrintLine(g_logformat,
-                                    std::string(entry.tag, entry.tagLen).c_str(),
-                                    entry.priority)) {
-        bool match = regexOk(entry);
+    if (android_log_shouldPrintLine(
+            context->logformat, std::string(entry.tag, entry.tagLen).c_str(),
+            entry.priority)) {
+        bool match = regexOk(context, entry);
 
-        g_printCount += match;
-        if (match || g_printItAnyways) {
-            bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
+        context->printCount += match;
+        if (match || context->printItAnyways) {
+            bytesWritten = android_log_printLogLine(context->logformat,
+                                                    context->output_fd, &entry);
 
             if (bytesWritten < 0) {
-                logcat_panic(HELP_FALSE, "output error");
+                logcat_panic(context, HELP_FALSE, "output error");
+                return;
             }
         }
     }
 
-    g_outByteCount += bytesWritten;
+    context->outByteCount += bytesWritten;
 
-    if (g_logRotateSizeKBytes > 0
-        && (g_outByteCount / 1024) >= g_logRotateSizeKBytes
-    ) {
-        rotateLogs();
+    if (context->logRotateSizeKBytes > 0 &&
+        (context->outByteCount / 1024) >= context->logRotateSizeKBytes) {
+        rotateLogs(context);
     }
-
-error:
-    return;
 }
 
-static void maybePrintStart(log_device_t* dev, bool printDividers) {
+static void maybePrintStart(android_logcat_context_internal* context,
+                            log_device_t* dev, bool printDividers) {
     if (!dev->printed || printDividers) {
-        if (g_devCount > 1 && !g_printBinary) {
+        if (context->devCount > 1 && !context->printBinary) {
             char buf[1024];
             snprintf(buf, sizeof(buf), "--------- %s %s\n",
-                     dev->printed ? "switch to" : "beginning of",
-                     dev->device);
-            if (write(g_outFD, buf, strlen(buf)) < 0) {
-                logcat_panic(HELP_FALSE, "output error");
+                     dev->printed ? "switch to" : "beginning of", dev->device);
+            if (write(context->output_fd, buf, strlen(buf)) < 0) {
+                logcat_panic(context, HELP_FALSE, "output error");
+                return;
             }
         }
         dev->printed = true;
     }
 }
 
-static void setupOutputAndSchedulingPolicy(bool blocking) {
-    if (g_outputFileName == NULL) {
-        g_outFD = STDOUT_FILENO;
-        return;
-    }
+static void setupOutputAndSchedulingPolicy(
+    android_logcat_context_internal* context, bool blocking) {
+    if (!context->outputFileName) return;
 
     if (blocking) {
         // Lower priority and set to batch scheduling if we are saving
         // the logs into files and taking continuous content.
-        if (set_sched_policy(0, SP_BACKGROUND) < 0) {
-            fprintf(stderr, "failed to set background scheduling policy\n");
+        if ((set_sched_policy(0, SP_BACKGROUND) < 0) && context->error) {
+            fprintf(context->error,
+                    "failed to set background scheduling policy\n");
         }
 
         struct sched_param param;
         memset(&param, 0, sizeof(param));
-        if (sched_setscheduler((pid_t) 0, SCHED_BATCH, &param) < 0) {
+        if (sched_setscheduler((pid_t)0, SCHED_BATCH, &param) < 0) {
             fprintf(stderr, "failed to set to batch scheduler\n");
         }
 
-        if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
-            fprintf(stderr, "failed set to priority\n");
+        if ((setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) &&
+            context->error) {
+            fprintf(context->error, "failed set to priority\n");
         }
     }
 
-    g_outFD = openLogFile (g_outputFileName);
+    close_output(context);
 
-    if (g_outFD < 0) {
-        logcat_panic(HELP_FALSE, "couldn't open output file");
+    context->output_fd = openLogFile(context->outputFileName);
+
+    if (context->output_fd < 0) {
+        logcat_panic(context, HELP_FALSE, "couldn't open output file");
+        return;
     }
 
     struct stat statbuf;
-    if (fstat(g_outFD, &statbuf) == -1) {
-        close(g_outFD);
-        logcat_panic(HELP_FALSE, "couldn't get output file stat\n");
+    if (fstat(context->output_fd, &statbuf) == -1) {
+        close_output(context);
+        logcat_panic(context, HELP_FALSE, "couldn't get output file stat\n");
+        return;
     }
 
-    if ((size_t) statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
-        close(g_outFD);
-        logcat_panic(HELP_FALSE, "invalid output file stat\n");
+    if ((size_t)statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
+        close_output(context);
+        logcat_panic(context, HELP_FALSE, "invalid output file stat\n");
+        return;
     }
 
-    g_outByteCount = statbuf.st_size;
+    context->output = fdopen(context->output_fd, "web");
+
+    context->outByteCount = statbuf.st_size;
 }
 
-static void show_help(const char *cmd)
-{
-    fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd);
+// clang-format off
+static void show_help(android_logcat_context_internal* context) {
+    if (!context->error) return;
 
-    fprintf(stderr, "options include:\n"
+    const char* cmd = strrchr(context->argv[0], '/');
+    cmd = cmd ? cmd + 1 : context->argv[0];
+
+    fprintf(context->error, "Usage: %s [options] [filterspecs]\n", cmd);
+
+    fprintf(context->error, "options include:\n"
                     "  -s              Set default filter to silent. Equivalent to filterspec '*:S'\n"
                     "  -f <file>, --file=<file>               Log to file. Default is stdout\n"
                     "  -r <kbytes>, --rotate-kbytes=<kbytes>\n"
@@ -298,6 +446,10 @@
                     "                  and individually flagged modifying adverbs can be added:\n"
                     "                    color descriptive epoch monotonic printable uid\n"
                     "                    usec UTC year zone\n"
+                    "                  Multiple -v parameters or comma separated list of format and\n"
+                    "                  format modifiers are allowed.\n"
+                    // private and undocumented nsec, no signal, too much noise
+                    // useful for -T or -t <timestamp> accurate testing though.
                     "  -D, --dividers  Print dividers between each log buffer\n"
                     "  -c, --clear     Clear (flush) the entire log and exit\n"
                     "                  if Log to File specified, clear fileset instead\n"
@@ -345,7 +497,7 @@
                     "                  comes first. Improves efficiency of polling by providing\n"
                     "                  an about-to-wrap wakeup.\n");
 
-    fprintf(stderr,"\nfilterspecs are a series of \n"
+    fprintf(context->error, "\nfilterspecs are a series of \n"
                    "  <tag>[:priority]\n\n"
                    "where <tag> is a log component tag (or * for all) and priority is:\n"
                    "  V    Verbose (default for <tag>)\n"
@@ -363,9 +515,9 @@
                    "or defaults to \"threadtime\"\n\n");
 }
 
-static void show_format_help()
-{
-    fprintf(stderr,
+static void show_format_help(android_logcat_context_internal* context) {
+    if (!context->error) return;
+    fprintf(context->error,
         "-v <format>, --format=<format> options:\n"
         "  Sets log print format verb and adverbs, where <format> is:\n"
         "    brief long process raw tag thread threadtime time\n"
@@ -396,141 +548,121 @@
         "  \"<zone>\"    — Print using this public named timezone (experimental).\n\n"
     );
 }
+// clang-format on
 
-static int setLogFormat(const char * formatString)
-{
-    static AndroidLogPrintFormat format;
+static int setLogFormat(android_logcat_context_internal* context,
+                        const char* formatString) {
+    AndroidLogPrintFormat format;
 
     format = android_log_formatFromString(formatString);
 
-    if (format == FORMAT_OFF) {
-        // FORMAT_OFF means invalid string
-        return -1;
-    }
+    // invalid string?
+    if (format == FORMAT_OFF) return -1;
 
-    return android_log_setPrintFormat(g_logformat, format);
+    return android_log_setPrintFormat(context->logformat, format);
 }
 
-static const char multipliers[][2] = {
-    { "" },
-    { "K" },
-    { "M" },
-    { "G" }
-};
+static const char multipliers[][2] = { { "" }, { "K" }, { "M" }, { "G" } };
 
-static unsigned long value_of_size(unsigned long value)
-{
+static unsigned long value_of_size(unsigned long value) {
     for (unsigned i = 0;
-            (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
-            value /= 1024, ++i) ;
+         (i < sizeof(multipliers) / sizeof(multipliers[0])) && (value >= 1024);
+         value /= 1024, ++i)
+        ;
     return value;
 }
 
-static const char *multiplier_of_size(unsigned long value)
-{
+static const char* multiplier_of_size(unsigned long value) {
     unsigned i;
     for (i = 0;
-            (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
-            value /= 1024, ++i) ;
+         (i < sizeof(multipliers) / sizeof(multipliers[0])) && (value >= 1024);
+         value /= 1024, ++i)
+        ;
     return multipliers[i];
 }
 
-/*String to unsigned int, returns -1 if it fails*/
-static bool getSizeTArg(const char *ptr, size_t *val, size_t min = 0,
-                        size_t max = SIZE_MAX)
-{
-    if (!ptr) {
-        return false;
-    }
+// String to unsigned int, returns -1 if it fails
+static bool getSizeTArg(const char* ptr, size_t* val, size_t min = 0,
+                        size_t max = SIZE_MAX) {
+    if (!ptr) return false;
 
-    char *endp;
+    char* endp;
     errno = 0;
     size_t ret = (size_t)strtoll(ptr, &endp, 0);
 
-    if (endp[0] || errno) {
-        return false;
-    }
+    if (endp[0] || errno) return false;
 
-    if ((ret > max) || (ret < min)) {
-        return false;
-    }
+    if ((ret > max) || (ret < min)) return false;
 
     *val = ret;
     return true;
 }
 
-static void logcat_panic(enum helpType showHelp, const char *fmt, ...)
-{
-    va_list  args;
+static void logcat_panic(android_logcat_context_internal* context,
+                         enum helpType showHelp, const char* fmt, ...) {
+    context->retval = EXIT_FAILURE;
+    if (!context->error) {
+        context->stop = true;
+        return;
+    }
+
+    va_list args;
     va_start(args, fmt);
-    vfprintf(stderr, fmt,  args);
+    vfprintf(context->error, fmt, args);
     va_end(args);
 
     switch (showHelp) {
-    case HELP_TRUE:
-       show_help(getprogname());
-       break;
-    case HELP_FORMAT:
-       show_format_help();
-       break;
-    case HELP_FALSE:
-    default:
-      break;
+        case HELP_TRUE:
+            show_help(context);
+            break;
+        case HELP_FORMAT:
+            show_format_help(context);
+            break;
+        case HELP_FALSE:
+        default:
+            break;
     }
 
-    exit(EXIT_FAILURE);
+    context->stop = true;
 }
 
-static char *parseTime(log_time &t, const char *cp) {
-
-    char *ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
-    if (ep) {
-        return ep;
-    }
+static char* parseTime(log_time& t, const char* cp) {
+    char* ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
+    if (ep) return ep;
     ep = t.strptime(cp, "%Y-%m-%d %H:%M:%S.%q");
-    if (ep) {
-        return ep;
-    }
+    if (ep) return ep;
     return t.strptime(cp, "%s.%q");
 }
 
 // Find last logged line in <outputFileName>, or <outputFileName>.1
-static log_time lastLogTime(char *outputFileName) {
+static log_time lastLogTime(const char* outputFileName) {
     log_time retval(log_time::EPOCH);
-    if (!outputFileName) {
-        return retval;
-    }
+    if (!outputFileName) return retval;
 
     std::string directory;
-    char *file = strrchr(outputFileName, '/');
+    const char* file = strrchr(outputFileName, '/');
     if (!file) {
         directory = ".";
         file = outputFileName;
     } else {
-        *file = '\0';
-        directory = outputFileName;
-        *file = '/';
+        directory = std::string(outputFileName, file - outputFileName);
         ++file;
     }
 
-    std::unique_ptr<DIR, int(*)(DIR*)>
-            dir(opendir(directory.c_str()), closedir);
-    if (!dir.get()) {
-        return retval;
-    }
+    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(directory.c_str()),
+                                            closedir);
+    if (!dir.get()) return retval;
 
     log_time now(android_log_clockid());
 
     size_t len = strlen(file);
     log_time modulo(0, NS_PER_SEC);
-    struct dirent *dp;
+    struct dirent* dp;
 
-    while ((dp = readdir(dir.get())) != NULL) {
-        if ((dp->d_type != DT_REG) ||
-                (strncmp(dp->d_name, file, len) != 0) ||
-                (dp->d_name[len] &&
-                     ((dp->d_name[len] != '.') ||
-                         (strtoll(dp->d_name + 1, NULL, 10) != 1)))) {
+    while (!!(dp = readdir(dir.get()))) {
+        if ((dp->d_type != DT_REG) || !!strncmp(dp->d_name, file, len) ||
+            (dp->d_name[len] && ((dp->d_name[len] != '.') ||
+                                 (strtoll(dp->d_name + 1, nullptr, 10) != 1)))) {
             continue;
         }
 
@@ -538,17 +670,13 @@
         file_name += "/";
         file_name += dp->d_name;
         std::string file;
-        if (!android::base::ReadFileToString(file_name, &file)) {
-            continue;
-        }
+        if (!android::base::ReadFileToString(file_name, &file)) continue;
 
         bool found = false;
         for (const auto& line : android::base::Split(file, "\n")) {
             log_time t(log_time::EPOCH);
-            char *ep = parseTime(t, line.c_str());
-            if (!ep || (*ep != ' ')) {
-                continue;
-            }
+            char* ep = parseTime(t, line.c_str());
+            if (!ep || (*ep != ' ')) continue;
             // determine the time precision of the logs (eg: msec or usec)
             for (unsigned long mod = 1UL; mod < modulo.tv_nsec; mod *= 10) {
                 if (t.tv_nsec % (mod * 10)) {
@@ -566,54 +694,170 @@
             }
         }
         // We count on the basename file to be the definitive end, so stop here.
-        if (!dp->d_name[len] && found) {
-            break;
-        }
+        if (!dp->d_name[len] && found) break;
     }
-    if (retval == log_time::EPOCH) {
-        return retval;
-    }
+    if (retval == log_time::EPOCH) return retval;
     // tail_time prints matching or higher, round up by the modulo to prevent
     // a replay of the last entry we have just checked.
     retval += modulo;
     return retval;
 }
 
-} /* namespace android */
+const char* getenv(android_logcat_context_internal* context, const char* name) {
+    if (!context->envp || !name || !*name) return nullptr;
 
+    for (size_t len = strlen(name), i = 0; context->envp[i]; ++i) {
+        if (strncmp(context->envp[i], name, len)) continue;
+        if (context->envp[i][len] == '=') return &context->envp[i][len + 1];
+    }
+    return nullptr;
+}
 
-int main(int argc, char **argv)
-{
+}  // namespace android
+
+void reportErrorName(const char** current, const char* name,
+                     bool blockSecurity) {
+    if (*current) return;
+    if (!blockSecurity || (android_name_to_log_id(name) != LOG_ID_SECURITY)) {
+        *current = name;
+    }
+}
+
+static int __logcat(android_logcat_context_internal* context) {
     using namespace android;
     int err;
-    int hasSetLogFormat = 0;
-    int clearLog = 0;
-    int getLogSize = 0;
-    unsigned long setLogSize = 0;
-    int getPruneList = 0;
-    char *setPruneList = NULL;
-    char *setId = NULL;
-    int printStatistics = 0;
-    int mode = ANDROID_LOG_RDONLY;
-    const char *forceFilters = NULL;
-    log_device_t* devices = NULL;
-    log_device_t* dev;
+    bool hasSetLogFormat = false;
+    bool clearLog = false;
+    bool allSelected = false;
+    bool getLogSize = false;
+    bool getPruneList = false;
+    bool printStatistics = false;
     bool printDividers = false;
-    struct logger_list *logger_list;
+    unsigned long setLogSize = 0;
+    const char* setPruneList = nullptr;
+    const char* setId = nullptr;
+    int mode = ANDROID_LOG_RDONLY;
+    std::string forceFilters;
+    log_device_t* devices = nullptr;
+    log_device_t* dev;
+    struct logger_list* logger_list;
     size_t tail_lines = 0;
     log_time tail_time(log_time::EPOCH);
     size_t pid = 0;
     bool got_t = false;
 
-    signal(SIGPIPE, exit);
+    // object instantiations before goto's can happen
+    log_device_t unexpected("unexpected", false);
+    const char* openDeviceFail = nullptr;
+    const char* clearFail = nullptr;
+    const char* setSizeFail = nullptr;
+    const char* getSizeFail = nullptr;
+    int argc = context->argc;
+    char* const* argv = context->argv;
 
-    g_logformat = android_log_format_new();
+    context->output = stdout;
+    context->error = stderr;
 
-    if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
-        show_help(argv[0]);
-        return EXIT_SUCCESS;
+    for (int i = 0; i < argc; ++i) {
+        // Simulate shell stderr redirect parsing
+        if ((argv[i][0] != '2') || (argv[i][1] != '>')) continue;
+
+        // Append to file not implemented, just open file
+        size_t skip = (argv[i][2] == '>') + 2;
+        if (!strcmp(&argv[i][skip], "/dev/null")) {
+            context->stderr_null = true;
+        } else if (!strcmp(&argv[i][skip], "&1")) {
+            context->stderr_stdout = true;
+        } else {
+            // stderr file redirections are not supported
+            fprintf(context->stderr_stdout ? stdout : stderr,
+                    "stderr redirection to file %s unsupported, skipping\n",
+                    &argv[i][skip]);
+        }
+        // Only the first one
+        break;
     }
 
+    const char* filename = nullptr;
+    for (int i = 0; i < argc; ++i) {
+        // Simulate shell stdout redirect parsing
+        if (argv[i][0] != '>') continue;
+
+        // Append to file not implemented, just open file
+        filename = &argv[i][(argv[i][1] == '>') + 1];
+        // Only the first one
+        break;
+    }
+
+    // Deal with setting up file descriptors and FILE pointers
+    if (context->error_fd >= 0) {  // Is an error file descriptor supplied?
+        if (context->error_fd == context->output_fd) {
+            context->stderr_stdout = true;
+        } else if (context->stderr_null) {  // redirection told us to close it
+            close(context->error_fd);
+            context->error_fd = -1;
+        } else {  // All Ok, convert error to a FILE pointer
+            context->error = fdopen(context->error_fd, "web");
+            if (!context->error) {
+                context->retval = -errno;
+                fprintf(context->stderr_stdout ? stdout : stderr,
+                        "Failed to fdopen(error_fd=%d) %s\n", context->error_fd,
+                        strerror(errno));
+                goto exit;
+            }
+        }
+    }
+    if (context->output_fd >= 0) {  // Is an output file descriptor supplied?
+        if (filename) {  // redirect to file, close supplied file descriptor.
+            close(context->output_fd);
+            context->output_fd = -1;
+        } else {  // All Ok, convert output to a FILE pointer
+            context->output = fdopen(context->output_fd, "web");
+            if (!context->output) {
+                context->retval = -errno;
+                fprintf(context->stderr_stdout ? stdout : context->error,
+                        "Failed to fdopen(output_fd=%d) %s\n",
+                        context->output_fd, strerror(errno));
+                goto exit;
+            }
+        }
+    }
+    if (filename) {  // We supplied an output file redirected in command line
+        context->output = fopen(filename, "web");
+    }
+    // Deal with 2>&1
+    if (context->stderr_stdout) context->error = context->output;
+    // Deal with 2>/dev/null
+    if (context->stderr_null) {
+        context->error_fd = -1;
+        context->error = nullptr;
+    }
+    // Only happens if output=stdout or output=filename
+    if ((context->output_fd < 0) && context->output) {
+        context->output_fd = fileno(context->output);
+    }
+    // Only happens if error=stdout || error=stderr
+    if ((context->error_fd < 0) && context->error) {
+        context->error_fd = fileno(context->error);
+    }
+
+    context->logformat = android_log_format_new();
+
+    if (argc == 2 && !strcmp(argv[1], "--help")) {
+        show_help(context);
+        context->retval = EXIT_SUCCESS;
+        goto exit;
+    }
+
+    // meant to catch comma-delimited values, but cast a wider
+    // net for stability dealing with possible mistaken inputs.
+    static const char delimiters[] = ",:; \t\n\r\f";
+
+    struct getopt_context optctx;
+    INIT_GETOPT_CONTEXT(optctx);
+    optctx.opterr = !!context->error;
+    optctx.optstderr = context->error;
+
     for (;;) {
         int ret;
 
@@ -624,66 +868,71 @@
         static const char id_str[] = "id";
         static const char wrap_str[] = "wrap";
         static const char print_str[] = "print";
+        // clang-format off
         static const struct option long_options[] = {
-          { "binary",        no_argument,       NULL,   'B' },
-          { "buffer",        required_argument, NULL,   'b' },
-          { "buffer-size",   optional_argument, NULL,   'g' },
-          { "clear",         no_argument,       NULL,   'c' },
-          { debug_str,       no_argument,       NULL,   0 },
-          { "dividers",      no_argument,       NULL,   'D' },
-          { "file",          required_argument, NULL,   'f' },
-          { "format",        required_argument, NULL,   'v' },
+          { "binary",        no_argument,       nullptr, 'B' },
+          { "buffer",        required_argument, nullptr, 'b' },
+          { "buffer-size",   optional_argument, nullptr, 'g' },
+          { "clear",         no_argument,       nullptr, 'c' },
+          { debug_str,       no_argument,       nullptr, 0 },
+          { "dividers",      no_argument,       nullptr, 'D' },
+          { "file",          required_argument, nullptr, 'f' },
+          { "format",        required_argument, nullptr, 'v' },
           // hidden and undocumented reserved alias for --regex
-          { "grep",          required_argument, NULL,   'e' },
+          { "grep",          required_argument, nullptr, 'e' },
           // hidden and undocumented reserved alias for --max-count
-          { "head",          required_argument, NULL,   'm' },
-          { id_str,          required_argument, NULL,   0 },
-          { "last",          no_argument,       NULL,   'L' },
-          { "max-count",     required_argument, NULL,   'm' },
-          { pid_str,         required_argument, NULL,   0 },
-          { print_str,       no_argument,       NULL,   0 },
-          { "prune",         optional_argument, NULL,   'p' },
-          { "regex",         required_argument, NULL,   'e' },
-          { "rotate-count",  required_argument, NULL,   'n' },
-          { "rotate-kbytes", required_argument, NULL,   'r' },
-          { "statistics",    no_argument,       NULL,   'S' },
+          { "head",          required_argument, nullptr, 'm' },
+          { id_str,          required_argument, nullptr, 0 },
+          { "last",          no_argument,       nullptr, 'L' },
+          { "max-count",     required_argument, nullptr, 'm' },
+          { pid_str,         required_argument, nullptr, 0 },
+          { print_str,       no_argument,       nullptr, 0 },
+          { "prune",         optional_argument, nullptr, 'p' },
+          { "regex",         required_argument, nullptr, 'e' },
+          { "rotate-count",  required_argument, nullptr, 'n' },
+          { "rotate-kbytes", required_argument, nullptr, 'r' },
+          { "statistics",    no_argument,       nullptr, 'S' },
           // hidden and undocumented reserved alias for -t
-          { "tail",          required_argument, NULL,   't' },
+          { "tail",          required_argument, nullptr, 't' },
           // support, but ignore and do not document, the optional argument
-          { wrap_str,        optional_argument, NULL,   0 },
-          { NULL,            0,                 NULL,   0 }
+          { wrap_str,        optional_argument, nullptr, 0 },
+          { nullptr,         0,                 nullptr, 0 }
         };
+        // clang-format on
 
-        ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
-                          long_options, &option_index);
-
-        if (ret < 0) {
-            break;
-        }
+        ret = getopt_long_r(argc, argv,
+                            ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
+                            &option_index, &optctx);
+        if (ret < 0) break;
 
         switch (ret) {
             case 0:
                 // only long options
                 if (long_options[option_index].name == pid_str) {
                     // ToDo: determine runtime PID_MAX?
-                    if (!getSizeTArg(optarg, &pid, 1)) {
-                        logcat_panic(HELP_TRUE, "%s %s out of range\n",
-                                     long_options[option_index].name, optarg);
+                    if (!getSizeTArg(optctx.optarg, &pid, 1)) {
+                        logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
+                                     long_options[option_index].name,
+                                     optctx.optarg);
+                        goto exit;
                     }
                     break;
                 }
                 if (long_options[option_index].name == wrap_str) {
-                    mode |= ANDROID_LOG_WRAP |
-                            ANDROID_LOG_RDONLY |
+                    mode |= ANDROID_LOG_WRAP | ANDROID_LOG_RDONLY |
                             ANDROID_LOG_NONBLOCK;
                     // ToDo: implement API that supports setting a wrap timeout
                     size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
-                    if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
-                        logcat_panic(HELP_TRUE, "%s %s out of range\n",
-                                     long_options[option_index].name, optarg);
+                    if (optctx.optarg &&
+                        !getSizeTArg(optctx.optarg, &dummy, 1)) {
+                        logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
+                                     long_options[option_index].name,
+                                     optctx.optarg);
+                        goto exit;
                     }
-                    if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
-                        fprintf(stderr,
+                    if ((dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) &&
+                        context->error) {
+                        fprintf(context->error,
                                 "WARNING: %s %u seconds, ignoring %zu\n",
                                 long_options[option_index].name,
                                 ANDROID_LOG_WRAP_DEFAULT_TIMEOUT, dummy);
@@ -691,169 +940,181 @@
                     break;
                 }
                 if (long_options[option_index].name == print_str) {
-                    g_printItAnyways = true;
+                    context->printItAnyways = true;
                     break;
                 }
                 if (long_options[option_index].name == debug_str) {
-                    g_debug = true;
+                    context->debug = true;
                     break;
                 }
                 if (long_options[option_index].name == id_str) {
-                    setId = optarg && optarg[0] ? optarg : NULL;
-                    break;
+                    setId = (optctx.optarg && optctx.optarg[0]) ? optctx.optarg
+                                                                : nullptr;
                 }
-            break;
+                break;
 
             case 's':
                 // default to all silent
-                android_log_addFilterRule(g_logformat, "*:s");
-            break;
+                android_log_addFilterRule(context->logformat, "*:s");
+                break;
 
             case 'c':
-                clearLog = 1;
+                clearLog = true;
                 mode |= ANDROID_LOG_WRONLY;
-            break;
+                break;
 
             case 'L':
-                mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK;
-            break;
+                mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE |
+                        ANDROID_LOG_NONBLOCK;
+                break;
 
             case 'd':
                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
-            break;
+                break;
 
             case 't':
                 got_t = true;
                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
-                /* FALLTHRU */
+            // FALLTHRU
             case 'T':
-                if (strspn(optarg, "0123456789") != strlen(optarg)) {
-                    char *cp = parseTime(tail_time, optarg);
+                if (strspn(optctx.optarg, "0123456789") !=
+                    strlen(optctx.optarg)) {
+                    char* cp = parseTime(tail_time, optctx.optarg);
                     if (!cp) {
-                        logcat_panic(HELP_FALSE,
-                                     "-%c \"%s\" not in time format\n",
-                                     ret, optarg);
+                        logcat_panic(context, HELP_FALSE,
+                                     "-%c \"%s\" not in time format\n", ret,
+                                     optctx.optarg);
+                        goto exit;
                     }
                     if (*cp) {
                         char c = *cp;
                         *cp = '\0';
-                        fprintf(stderr,
+                        if (context->error) {
+                            fprintf(
+                                context->error,
                                 "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
-                                ret, optarg, c, cp + 1);
+                                ret, optctx.optarg, c, cp + 1);
+                        }
                         *cp = c;
                     }
                 } else {
-                    if (!getSizeTArg(optarg, &tail_lines, 1)) {
-                        fprintf(stderr,
-                                "WARNING: -%c %s invalid, setting to 1\n",
-                                ret, optarg);
+                    if (!getSizeTArg(optctx.optarg, &tail_lines, 1)) {
+                        if (context->error) {
+                            fprintf(context->error,
+                                    "WARNING: -%c %s invalid, setting to 1\n",
+                                    ret, optctx.optarg);
+                        }
                         tail_lines = 1;
                     }
                 }
-            break;
+                break;
 
             case 'D':
                 printDividers = true;
-            break;
+                break;
 
             case 'e':
-                g_regex = new pcrecpp::RE(optarg);
-            break;
+                context->regex = new pcrecpp::RE(optctx.optarg);
+                break;
 
             case 'm': {
-                char *end = NULL;
-                if (!getSizeTArg(optarg, &g_maxCount)) {
-                    logcat_panic(HELP_FALSE, "-%c \"%s\" isn't an "
-                                 "integer greater than zero\n", ret, optarg);
+                char* end = nullptr;
+                if (!getSizeTArg(optctx.optarg, &context->maxCount)) {
+                    logcat_panic(context, HELP_FALSE,
+                                 "-%c \"%s\" isn't an "
+                                 "integer greater than zero\n",
+                                 ret, optctx.optarg);
+                    goto exit;
                 }
-            }
-            break;
+            } break;
 
             case 'g':
-                if (!optarg) {
-                    getLogSize = 1;
+                if (!optctx.optarg) {
+                    getLogSize = true;
                     break;
                 }
-                // FALLTHRU
+            // FALLTHRU
 
             case 'G': {
-                char *cp;
-                if (strtoll(optarg, &cp, 0) > 0) {
-                    setLogSize = strtoll(optarg, &cp, 0);
+                char* cp;
+                if (strtoll(optctx.optarg, &cp, 0) > 0) {
+                    setLogSize = strtoll(optctx.optarg, &cp, 0);
                 } else {
                     setLogSize = 0;
                 }
 
-                switch(*cp) {
-                case 'g':
-                case 'G':
-                    setLogSize *= 1024;
-                /* FALLTHRU */
-                case 'm':
-                case 'M':
-                    setLogSize *= 1024;
-                /* FALLTHRU */
-                case 'k':
-                case 'K':
-                    setLogSize *= 1024;
-                /* FALLTHRU */
-                case '\0':
-                break;
+                switch (*cp) {
+                    case 'g':
+                    case 'G':
+                        setLogSize *= 1024;
+                    // FALLTHRU
+                    case 'm':
+                    case 'M':
+                        setLogSize *= 1024;
+                    // FALLTHRU
+                    case 'k':
+                    case 'K':
+                        setLogSize *= 1024;
+                    // FALLTHRU
+                    case '\0':
+                        break;
 
-                default:
-                    setLogSize = 0;
+                    default:
+                        setLogSize = 0;
                 }
 
                 if (!setLogSize) {
-                    fprintf(stderr, "ERROR: -G <num><multiplier>\n");
-                    return EXIT_FAILURE;
+                    logcat_panic(context, HELP_FALSE,
+                                 "ERROR: -G <num><multiplier>\n");
+                    goto exit;
                 }
-            }
-            break;
+            } break;
 
             case 'p':
-                if (!optarg) {
-                    getPruneList = 1;
+                if (!optctx.optarg) {
+                    getPruneList = true;
                     break;
                 }
-                // FALLTHRU
+            // FALLTHRU
 
             case 'P':
-                setPruneList = optarg;
-            break;
+                setPruneList = optctx.optarg;
+                break;
 
             case 'b': {
+                std::unique_ptr<char, void (*)(void*)> buffers(
+                    strdup(optctx.optarg), free);
+                char* arg = buffers.get();
                 unsigned idMask = 0;
-                while ((optarg = strtok(optarg, ",:; \t\n\r\f")) != NULL) {
-                    if (strcmp(optarg, "default") == 0) {
-                        idMask |= (1 << LOG_ID_MAIN) |
-                                  (1 << LOG_ID_SYSTEM) |
+                char* sv = nullptr;  // protect against -ENOMEM above
+                while (!!(arg = strtok_r(arg, delimiters, &sv))) {
+                    if (!strcmp(arg, "default")) {
+                        idMask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) |
                                   (1 << LOG_ID_CRASH);
-                    } else if (strcmp(optarg, "all") == 0) {
+                    } else if (!strcmp(arg, "all")) {
+                        allSelected = true;
                         idMask = (unsigned)-1;
                     } else {
-                        log_id_t log_id = android_name_to_log_id(optarg);
-                        const char *name = android_log_id_to_name(log_id);
+                        log_id_t log_id = android_name_to_log_id(arg);
+                        const char* name = android_log_id_to_name(log_id);
 
-                        if (strcmp(name, optarg) != 0) {
-                            logcat_panic(HELP_TRUE,
-                                         "unknown buffer %s\n", optarg);
+                        if (!!strcmp(name, arg)) {
+                            logcat_panic(context, HELP_TRUE,
+                                         "unknown buffer %s\n", arg);
+                            goto exit;
                         }
+                        if (log_id == LOG_ID_SECURITY) allSelected = false;
                         idMask |= (1 << log_id);
                     }
-                    optarg = NULL;
+                    arg = nullptr;
                 }
 
                 for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
-                    const char *name = android_log_id_to_name((log_id_t)i);
+                    const char* name = android_log_id_to_name((log_id_t)i);
                     log_id_t log_id = android_name_to_log_id(name);
 
-                    if (log_id != (log_id_t)i) {
-                        continue;
-                    }
-                    if ((idMask & (1 << i)) == 0) {
-                        continue;
-                    }
+                    if (log_id != (log_id_t)i) continue;
+                    if (!(idMask & (1 << i))) continue;
 
                     bool found = false;
                     for (dev = devices; dev; dev = dev->next) {
@@ -861,16 +1122,12 @@
                             found = true;
                             break;
                         }
-                        if (!dev->next) {
-                            break;
-                        }
+                        if (!dev->next) break;
                     }
-                    if (found) {
-                        continue;
-                    }
+                    if (found) continue;
 
-                    bool binary = !strcmp(name, "events") ||
-                                  !strcmp(name, "security");
+                    bool binary =
+                        !strcmp(name, "events") || !strcmp(name, "security");
                     log_device_t* d = new log_device_t(name, binary);
 
                     if (dev) {
@@ -879,220 +1136,250 @@
                     } else {
                         devices = dev = d;
                     }
-                    g_devCount++;
+                    context->devCount++;
                 }
-            }
-            break;
+            } break;
 
             case 'B':
-                g_printBinary = 1;
-            break;
+                context->printBinary = 1;
+                break;
 
             case 'f':
-                if ((tail_time == log_time::EPOCH) && (tail_lines == 0)) {
-                    tail_time = lastLogTime(optarg);
+                if ((tail_time == log_time::EPOCH) && !tail_lines) {
+                    tail_time = lastLogTime(optctx.optarg);
                 }
                 // redirect output to a file
-                g_outputFileName = optarg;
-            break;
+                context->outputFileName = optctx.optarg;
+                break;
 
             case 'r':
-                if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) {
-                    logcat_panic(HELP_TRUE,
-                                 "Invalid parameter \"%s\" to -r\n", optarg);
+                if (!getSizeTArg(optctx.optarg, &context->logRotateSizeKBytes,
+                                 1)) {
+                    logcat_panic(context, HELP_TRUE,
+                                 "Invalid parameter \"%s\" to -r\n",
+                                 optctx.optarg);
+                    goto exit;
                 }
-            break;
+                break;
 
             case 'n':
-                if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) {
-                    logcat_panic(HELP_TRUE,
-                                 "Invalid parameter \"%s\" to -n\n", optarg);
+                if (!getSizeTArg(optctx.optarg, &context->maxRotatedLogs, 1)) {
+                    logcat_panic(context, HELP_TRUE,
+                                 "Invalid parameter \"%s\" to -n\n",
+                                 optctx.optarg);
+                    goto exit;
                 }
-            break;
+                break;
 
-            case 'v':
-                if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
-                    show_format_help();
-                    exit(0);
+            case 'v': {
+                if (!strcmp(optctx.optarg, "help") ||
+                    !strcmp(optctx.optarg, "--help")) {
+                    show_format_help(context);
+                    context->retval = EXIT_SUCCESS;
+                    goto exit;
                 }
-                err = setLogFormat(optarg);
-                if (err < 0) {
-                    logcat_panic(HELP_FORMAT,
-                                 "Invalid parameter \"%s\" to -v\n", optarg);
+                std::unique_ptr<char, void (*)(void*)> formats(
+                    strdup(optctx.optarg), free);
+                char* arg = formats.get();
+                unsigned idMask = 0;
+                char* sv = nullptr;  // protect against -ENOMEM above
+                while (!!(arg = strtok_r(arg, delimiters, &sv))) {
+                    err = setLogFormat(context, arg);
+                    if (err < 0) {
+                        logcat_panic(context, HELP_FORMAT,
+                                     "Invalid parameter \"%s\" to -v\n", arg);
+                        goto exit;
+                    }
+                    arg = nullptr;
+                    if (err) hasSetLogFormat = true;
                 }
-                hasSetLogFormat |= err;
-            break;
+            } break;
 
             case 'Q':
-                /* this is a *hidden* option used to start a version of logcat                 */
-                /* in an emulated device only. it basically looks for androidboot.logcat=      */
-                /* on the kernel command line. If something is found, it extracts a log filter */
-                /* and uses it to run the program. If nothing is found, the program should     */
-                /* quit immediately                                                            */
-#define  KERNEL_OPTION  "androidboot.logcat="
-#define  CONSOLE_OPTION "androidboot.console="
+#define KERNEL_OPTION "androidboot.logcat="
+#define CONSOLE_OPTION "androidboot.console="
+                // This is a *hidden* option used to start a version of logcat
+                // in an emulated device only.  It basically looks for
+                // androidboot.logcat= on the kernel command line.  If
+                // something is found, it extracts a log filter and uses it to
+                // run the program.  If nothing is found, the program should
+                // quit immediately.
                 {
-                    int          fd;
-                    char*        logcat;
-                    char*        console;
-                    int          force_exit = 1;
-                    static char  cmdline[1024];
+                    std::string cmdline;
+                    android::base::ReadFileToString("/proc/cmdline", &cmdline);
 
-                    fd = open("/proc/cmdline", O_RDONLY);
-                    if (fd >= 0) {
-                        int  n = read(fd, cmdline, sizeof(cmdline)-1 );
-                        if (n < 0) n = 0;
-                        cmdline[n] = 0;
-                        close(fd);
-                    } else {
-                        cmdline[0] = 0;
+                    const char* logcat = strstr(cmdline.c_str(), KERNEL_OPTION);
+                    // if nothing found or invalid filters, exit quietly
+                    if (!logcat) {
+                        context->retval = EXIT_SUCCESS;
+                        goto exit;
                     }
 
-                    logcat  = strstr( cmdline, KERNEL_OPTION );
-                    console = strstr( cmdline, CONSOLE_OPTION );
-                    if (logcat != NULL) {
-                        char*  p = logcat + sizeof(KERNEL_OPTION)-1;;
-                        char*  q = strpbrk( p, " \t\n\r" );;
+                    const char* p = logcat + strlen(KERNEL_OPTION);
+                    const char* q = strpbrk(p, " \t\n\r");
+                    if (!q) q = p + strlen(p);
+                    forceFilters = std::string(p, q);
 
-                        if (q != NULL)
-                            *q = 0;
+                    // redirect our output to the emulator console
+                    const char* console =
+                        strstr(cmdline.c_str(), CONSOLE_OPTION);
+                    if (!console) break;
 
-                        forceFilters = p;
-                        force_exit   = 0;
-                    }
-                    /* if nothing found or invalid filters, exit quietly */
-                    if (force_exit) {
-                        return EXIT_SUCCESS;
+                    p = console + strlen(CONSOLE_OPTION);
+                    q = strpbrk(p, " \t\n\r");
+                    int len = q ? q - p : strlen(p);
+                    std::string devname = "/dev/" + std::string(p, len);
+                    cmdline.erase();
+
+                    if (context->error) {
+                        fprintf(context->error, "logcat using %s\n",
+                                devname.c_str());
                     }
 
-                    /* redirect our output to the emulator console */
-                    if (console) {
-                        char*  p = console + sizeof(CONSOLE_OPTION)-1;
-                        char*  q = strpbrk( p, " \t\n\r" );
-                        char   devname[64];
-                        int    len;
+                    FILE* fp = fopen(devname.c_str(), "web");
+                    devname.erase();
+                    if (!fp) break;
 
-                        if (q != NULL) {
-                            len = q - p;
-                        } else
-                            len = strlen(p);
-
-                        len = snprintf( devname, sizeof(devname), "/dev/%.*s", len, p );
-                        fprintf(stderr, "logcat using %s (%d)\n", devname, len);
-                        if (len < (int)sizeof(devname)) {
-                            fd = open( devname, O_WRONLY );
-                            if (fd >= 0) {
-                                dup2(fd, 1);
-                                dup2(fd, 2);
-                                close(fd);
-                            }
-                        }
-                    }
+                    // close output and error channels, replace with console
+                    android::close_output(context);
+                    android::close_error(context);
+                    context->stderr_stdout = true;
+                    context->output = fp;
+                    context->output_fd = fileno(fp);
+                    if (context->stderr_null) break;
+                    context->stderr_stdout = true;
+                    context->error = fp;
+                    context->error_fd = fileno(fp);
                 }
                 break;
 
             case 'S':
-                printStatistics = 1;
+                printStatistics = true;
                 break;
 
             case ':':
-                logcat_panic(HELP_TRUE,
-                             "Option -%c needs an argument\n", optopt);
-                break;
+                logcat_panic(context, HELP_TRUE,
+                             "Option -%c needs an argument\n", optctx.optopt);
+                goto exit;
 
             default:
-                logcat_panic(HELP_TRUE,
-                             "Unrecognized Option %c\n", optopt);
-                break;
+                logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n",
+                             optctx.optopt);
+                goto exit;
         }
     }
 
-    if (g_maxCount && got_t) {
-        logcat_panic(HELP_TRUE,
+    if (context->maxCount && got_t) {
+        logcat_panic(context, HELP_TRUE,
                      "Cannot use -m (--max-count) and -t together\n");
+        goto exit;
     }
-    if (g_printItAnyways && (!g_regex || !g_maxCount)) {
+    if (context->printItAnyways && (!context->regex || !context->maxCount)) {
         // One day it would be nice if --print -v color and --regex <expr>
         // could play with each other and show regex highlighted content.
-        fprintf(stderr, "WARNING: "
+        // clang-format off
+        if (context->error) {
+            fprintf(context->error, "WARNING: "
                             "--print ignored, to be used in combination with\n"
-                        "         "
+                                "         "
                             "--regex <expr> and --max-count <N>\n");
-        g_printItAnyways = false;
+        }
+        context->printItAnyways = false;
     }
 
     if (!devices) {
         dev = devices = new log_device_t("main", false);
-        g_devCount = 1;
+        context->devCount = 1;
         if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
             dev = dev->next = new log_device_t("system", false);
-            g_devCount++;
+            context->devCount++;
         }
         if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
             dev = dev->next = new log_device_t("crash", false);
-            g_devCount++;
+            context->devCount++;
         }
     }
 
-    if (g_logRotateSizeKBytes != 0 && g_outputFileName == NULL) {
-        logcat_panic(HELP_TRUE, "-r requires -f as well\n");
+    if (!!context->logRotateSizeKBytes && !context->outputFileName) {
+        logcat_panic(context, HELP_TRUE, "-r requires -f as well\n");
+        goto exit;
     }
 
-    if (setId != NULL) {
-        if (g_outputFileName == NULL) {
-            logcat_panic(HELP_TRUE, "--id='%s' requires -f as well\n", setId);
+    if (!!setId) {
+        if (!context->outputFileName) {
+            logcat_panic(context, HELP_TRUE,
+                         "--id='%s' requires -f as well\n", setId);
+            goto exit;
         }
 
-        std::string file_name = android::base::StringPrintf("%s.id", g_outputFileName);
+        std::string file_name = android::base::StringPrintf(
+                                        "%s.id", context->outputFileName);
         std::string file;
         bool file_ok = android::base::ReadFileToString(file_name, &file);
-        android::base::WriteStringToFile(setId, file_name,
-                                         S_IRUSR | S_IWUSR, getuid(), getgid());
-        if (!file_ok || (file.compare(setId) == 0)) {
-            setId = NULL;
-        }
+        android::base::WriteStringToFile(setId, file_name, S_IRUSR | S_IWUSR,
+                                         getuid(), getgid());
+        if (!file_ok || !file.compare(setId)) setId = nullptr;
     }
 
-    if (hasSetLogFormat == 0) {
-        const char* logFormat = getenv("ANDROID_PRINTF_LOG");
+    if (!hasSetLogFormat) {
+        const char* logFormat = android::getenv(context, "ANDROID_PRINTF_LOG");
 
-        if (logFormat != NULL) {
-            err = setLogFormat(logFormat);
-            if (err < 0) {
-                fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n",
-                                    logFormat);
+        if (!!logFormat) {
+            std::unique_ptr<char, void (*)(void*)> formats(strdup(logFormat),
+                                                           free);
+            char* sv = nullptr;  // protect against -ENOMEM above
+            char* arg = formats.get();
+            while (!!(arg = strtok_r(arg, delimiters, &sv))) {
+                err = setLogFormat(context, arg);
+                // environment should not cause crash of logcat
+                if ((err < 0) && context->error) {
+                    fprintf(context->error,
+                            "invalid format in ANDROID_PRINTF_LOG '%s'\n", arg);
+                }
+                arg = nullptr;
+                if (err > 0) hasSetLogFormat = true;
             }
-        } else {
-            setLogFormat("threadtime");
+        }
+        if (!hasSetLogFormat) {
+            setLogFormat(context, "threadtime");
         }
     }
 
-    if (forceFilters) {
-        err = android_log_addFilterString(g_logformat, forceFilters);
+    if (forceFilters.size()) {
+        err = android_log_addFilterString(context->logformat,
+                                          forceFilters.c_str());
         if (err < 0) {
-            logcat_panic(HELP_FALSE,
+            logcat_panic(context, HELP_FALSE,
                          "Invalid filter expression in logcat args\n");
+            goto exit;
         }
-    } else if (argc == optind) {
+    } else if (argc == optctx.optind) {
         // Add from environment variable
-        char *env_tags_orig = getenv("ANDROID_LOG_TAGS");
+        const char* env_tags_orig = android::getenv(context, "ANDROID_LOG_TAGS");
 
-        if (env_tags_orig != NULL) {
-            err = android_log_addFilterString(g_logformat, env_tags_orig);
+        if (!!env_tags_orig) {
+            err = android_log_addFilterString(context->logformat,
+                                              env_tags_orig);
 
             if (err < 0) {
-                logcat_panic(HELP_TRUE,
+                logcat_panic(context, HELP_TRUE,
                             "Invalid filter expression in ANDROID_LOG_TAGS\n");
+                goto exit;
             }
         }
     } else {
         // Add from commandline
-        for (int i = optind ; i < argc ; i++) {
-            err = android_log_addFilterString(g_logformat, argv[i]);
+        for (int i = optctx.optind ; i < argc ; i++) {
+            // skip stderr redirections of _all_ kinds
+            if ((argv[i][0] == '2') && (argv[i][1] == '>')) continue;
+            // skip stdout redirections of _all_ kinds
+            if (argv[i][0] == '>') continue;
 
+            err = android_log_addFilterString(context->logformat, argv[i]);
             if (err < 0) {
-                logcat_panic(HELP_TRUE,
+                logcat_panic(context, HELP_TRUE,
                              "Invalid filter expression '%s'\n", argv[i]);
+                goto exit;
             }
         }
     }
@@ -1103,10 +1390,6 @@
     } else {
         logger_list = android_logger_list_alloc(mode, tail_lines, pid);
     }
-    const char *openDeviceFail = NULL;
-    const char *clearFail = NULL;
-    const char *setSizeFail = NULL;
-    const char *getSizeFail = NULL;
     // We have three orthogonal actions below to clear, set log size and
     // get log size. All sharing the same iteration loop.
     while (dev) {
@@ -1114,47 +1397,50 @@
         dev->logger = android_logger_open(logger_list,
                                           android_name_to_log_id(dev->device));
         if (!dev->logger) {
-            openDeviceFail = openDeviceFail ?: dev->device;
+            reportErrorName(&openDeviceFail, dev->device, allSelected);
             dev = dev->next;
             continue;
         }
 
         if (clearLog || setId) {
-            if (g_outputFileName) {
+            if (context->outputFileName) {
                 int maxRotationCountDigits =
-                    (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
+                    (context->maxRotatedLogs > 0) ?
+                        (int)(floor(log10(context->maxRotatedLogs) + 1)) :
+                        0;
 
-                for (int i = g_maxRotatedLogs ; i >= 0 ; --i) {
+                for (int i = context->maxRotatedLogs ; i >= 0 ; --i) {
                     std::string file;
 
-                    if (i == 0) {
-                        file = android::base::StringPrintf("%s", g_outputFileName);
+                    if (!i) {
+                        file = android::base::StringPrintf(
+                            "%s", context->outputFileName);
                     } else {
                         file = android::base::StringPrintf("%s.%.*d",
-                            g_outputFileName, maxRotationCountDigits, i);
+                            context->outputFileName, maxRotationCountDigits, i);
                     }
 
-                    if (file.length() == 0) {
+                    if (!file.length()) {
                         perror("while clearing log files");
-                        clearFail = clearFail ?: dev->device;
+                        reportErrorName(&clearFail, dev->device, allSelected);
                         break;
                     }
 
                     err = unlink(file.c_str());
 
-                    if (err < 0 && errno != ENOENT && clearFail == NULL) {
+                    if (err < 0 && errno != ENOENT && !clearFail) {
                         perror("while clearing log files");
-                        clearFail = dev->device;
+                        reportErrorName(&clearFail, dev->device, allSelected);
                     }
                 }
             } else if (android_logger_clear(dev->logger)) {
-                clearFail = clearFail ?: dev->device;
+                reportErrorName(&clearFail, dev->device, allSelected);
             }
         }
 
         if (setLogSize) {
             if (android_logger_set_log_size(dev->logger, setLogSize)) {
-                setSizeFail = setSizeFail ?: dev->device;
+                reportErrorName(&setSizeFail, dev->device, allSelected);
             }
         }
 
@@ -1163,69 +1449,82 @@
             long readable = android_logger_get_log_readable_size(dev->logger);
 
             if ((size < 0) || (readable < 0)) {
-                getSizeFail = getSizeFail ?: dev->device;
+                reportErrorName(&getSizeFail, dev->device, allSelected);
             } else {
-                printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
-                       "max entry is %db, max payload is %db\n", dev->device,
+                std::string str = android::base::StringPrintf(
+                       "%s: ring buffer is %ld%sb (%ld%sb consumed),"
+                         " max entry is %db, max payload is %db\n",
+                       dev->device,
                        value_of_size(size), multiplier_of_size(size),
                        value_of_size(readable), multiplier_of_size(readable),
-                       (int) LOGGER_ENTRY_MAX_LEN,
-                       (int) LOGGER_ENTRY_MAX_PAYLOAD);
+                       (int)LOGGER_ENTRY_MAX_LEN,
+                       (int)LOGGER_ENTRY_MAX_PAYLOAD);
+                TEMP_FAILURE_RETRY(write(context->output_fd,
+                                         str.data(), str.length()));
             }
         }
 
         dev = dev->next;
     }
+
+    context->retval = EXIT_SUCCESS;
+
     // report any errors in the above loop and exit
     if (openDeviceFail) {
-        logcat_panic(HELP_FALSE,
+        logcat_panic(context, HELP_FALSE,
                      "Unable to open log device '%s'\n", openDeviceFail);
+        goto close;
     }
     if (clearFail) {
-        logcat_panic(HELP_FALSE,
+        logcat_panic(context, HELP_FALSE,
                      "failed to clear the '%s' log\n", clearFail);
+        goto close;
     }
     if (setSizeFail) {
-        logcat_panic(HELP_FALSE,
+        logcat_panic(context, HELP_FALSE,
                      "failed to set the '%s' log size\n", setSizeFail);
+        goto close;
     }
     if (getSizeFail) {
-        logcat_panic(HELP_FALSE,
+        logcat_panic(context, HELP_FALSE,
                      "failed to get the readable '%s' log size", getSizeFail);
+        goto close;
     }
 
     if (setPruneList) {
         size_t len = strlen(setPruneList);
-        /*extra 32 bytes are needed by  android_logger_set_prune_list */
+        // extra 32 bytes are needed by android_logger_set_prune_list
         size_t bLen = len + 32;
-        char *buf = NULL;
+        char* buf = nullptr;
         if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
             buf[len] = '\0';
             if (android_logger_set_prune_list(logger_list, buf, bLen)) {
-                logcat_panic(HELP_FALSE, "failed to set the prune list");
+                logcat_panic(context, HELP_FALSE,
+                             "failed to set the prune list");
             }
             free(buf);
         } else {
-            logcat_panic(HELP_FALSE, "failed to set the prune list (alloc)");
+            logcat_panic(context, HELP_FALSE,
+                         "failed to set the prune list (alloc)");
         }
+        goto close;
     }
 
     if (printStatistics || getPruneList) {
         size_t len = 8192;
-        char *buf;
+        char* buf;
 
-        for (int retry = 32;
-                (retry >= 0) && ((buf = new char [len]));
-                delete [] buf, buf = NULL, --retry) {
+        for (int retry = 32; (retry >= 0) && ((buf = new char[len]));
+             delete[] buf, buf = nullptr, --retry) {
             if (getPruneList) {
                 android_logger_get_prune_list(logger_list, buf, len);
             } else {
                 android_logger_get_statistics(logger_list, buf, len);
             }
-            buf[len-1] = '\0';
+            buf[len - 1] = '\0';
             if (atol(buf) < 3) {
-                delete [] buf;
-                buf = NULL;
+                delete[] buf;
+                buf = nullptr;
                 break;
             }
             size_t ret = atol(buf) + 1;
@@ -1237,98 +1536,263 @@
         }
 
         if (!buf) {
-            logcat_panic(HELP_FALSE, "failed to read data");
+            logcat_panic(context, HELP_FALSE, "failed to read data");
+            goto close;
         }
 
         // remove trailing FF
-        char *cp = buf + len - 1;
+        char* cp = buf + len - 1;
         *cp = '\0';
         bool truncated = *--cp != '\f';
-        if (!truncated) {
-            *cp = '\0';
-        }
+        if (!truncated) *cp = '\0';
 
         // squash out the byte count
         cp = buf;
         if (!truncated) {
-            while (isdigit(*cp)) {
-                ++cp;
-            }
-            if (*cp == '\n') {
-                ++cp;
-            }
+            while (isdigit(*cp)) ++cp;
+            if (*cp == '\n') ++cp;
         }
 
-        printf("%s", cp);
-        delete [] buf;
-        return EXIT_SUCCESS;
+        len = strlen(cp);
+        TEMP_FAILURE_RETRY(write(context->output_fd, cp, len));
+        delete[] buf;
+        goto close;
     }
 
-    if (getLogSize) {
-        return EXIT_SUCCESS;
-    }
-    if (setLogSize || setPruneList) {
-        return EXIT_SUCCESS;
-    }
-    if (clearLog) {
-        return EXIT_SUCCESS;
-    }
+    if (getLogSize || setLogSize || clearLog) goto close;
 
-    setupOutputAndSchedulingPolicy((mode & ANDROID_LOG_NONBLOCK) == 0);
+    setupOutputAndSchedulingPolicy(context, !(mode & ANDROID_LOG_NONBLOCK));
+    if (context->stop) goto close;
 
-    //LOG_EVENT_INT(10, 12345);
-    //LOG_EVENT_LONG(11, 0x1122334455667788LL);
-    //LOG_EVENT_STRING(0, "whassup, doc?");
+    // LOG_EVENT_INT(10, 12345);
+    // LOG_EVENT_LONG(11, 0x1122334455667788LL);
+    // LOG_EVENT_STRING(0, "whassup, doc?");
 
-    dev = NULL;
-    log_device_t unexpected("unexpected", false);
+    dev = nullptr;
 
-    while (!g_maxCount || (g_printCount < g_maxCount)) {
+    while (!context->stop &&
+           (!context->maxCount || (context->printCount < context->maxCount))) {
         struct log_msg log_msg;
-        log_device_t* d;
         int ret = android_logger_list_read(logger_list, &log_msg);
-
-        if (ret == 0) {
-            logcat_panic(HELP_FALSE, "read: unexpected EOF!\n");
+        if (!ret) {
+            logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
+            break;
         }
 
         if (ret < 0) {
-            if (ret == -EAGAIN) {
-                break;
-            }
+            if (ret == -EAGAIN) break;
 
             if (ret == -EIO) {
-                logcat_panic(HELP_FALSE, "read: unexpected EOF!\n");
-            }
-            if (ret == -EINVAL) {
-                logcat_panic(HELP_FALSE, "read: unexpected length.\n");
-            }
-            logcat_panic(HELP_FALSE, "logcat read failure");
-        }
-
-        for (d = devices; d; d = d->next) {
-            if (android_name_to_log_id(d->device) == log_msg.id()) {
+                logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
                 break;
             }
+            if (ret == -EINVAL) {
+                logcat_panic(context, HELP_FALSE, "read: unexpected length.\n");
+                break;
+            }
+            logcat_panic(context, HELP_FALSE, "logcat read failure");
+            break;
+        }
+
+        log_device_t* d;
+        for (d = devices; d; d = d->next) {
+            if (android_name_to_log_id(d->device) == log_msg.id()) break;
         }
         if (!d) {
-            g_devCount = 2; // set to Multiple
+            context->devCount = 2; // set to Multiple
             d = &unexpected;
             d->binary = log_msg.id() == LOG_ID_EVENTS;
         }
 
         if (dev != d) {
             dev = d;
-            maybePrintStart(dev, printDividers);
+            maybePrintStart(context, dev, printDividers);
+            if (context->stop) break;
         }
-        if (g_printBinary) {
-            printBinary(&log_msg);
+        if (context->printBinary) {
+            printBinary(context, &log_msg);
         } else {
-            processBuffer(dev, &log_msg);
+            processBuffer(context, dev, &log_msg);
         }
     }
 
+close:
     android_logger_list_free(logger_list);
 
-    return EXIT_SUCCESS;
+exit:
+    // close write end of pipe to help things along
+    if (context->output_fd == context->fds[1]) {
+        android::close_output(context);
+    }
+    if (context->error_fd == context->fds[1]) {
+        android::close_error(context);
+    }
+    if (context->fds[1] >= 0) {
+        // NB: should be closed by the above
+        int save_errno = errno;
+        close(context->fds[1]);
+        errno = save_errno;
+        context->fds[1] = -1;
+    }
+    context->thread_stopped = true;
+    return context->retval;
+}
+
+// Can block
+int android_logcat_run_command(android_logcat_context ctx,
+                               int output, int error,
+                               int argc, char* const* argv,
+                               char* const* envp) {
+    android_logcat_context_internal* context = ctx;
+
+    context->output_fd = output;
+    context->error_fd = error;
+    context->argc = argc;
+    context->argv = argv;
+    context->envp = envp;
+    context->stop = false;
+    context->thread_stopped = false;
+    return __logcat(context);
+}
+
+// starts a thread, opens a pipe, returns reading end.
+int android_logcat_run_command_thread(android_logcat_context ctx,
+                                      int argc, char* const* argv,
+                                      char* const* envp) {
+    android_logcat_context_internal* context = ctx;
+
+    int save_errno = EBUSY;
+    if ((context->fds[0] >= 0) || (context->fds[1] >= 0)) goto exit;
+
+    if (pipe(context->fds) < 0) {
+        save_errno = errno;
+        goto exit;
+    }
+
+    pthread_attr_t attr;
+    if (pthread_attr_init(&attr)) {
+        save_errno = errno;
+        goto close_exit;
+    }
+
+    struct sched_param param;
+    memset(&param, 0, sizeof(param));
+    pthread_attr_setschedparam(&attr, &param);
+    pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
+    if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
+        int save_errno = errno;
+        goto pthread_attr_exit;
+    }
+
+    context->stop = false;
+    context->thread_stopped = false;
+    context->output_fd = context->fds[1];
+    // save off arguments so they remain while thread is active.
+    for (int i = 0; i < argc; ++i) {
+        context->args.push_back(std::string(argv[i]));
+    }
+    // save off environment so they remain while thread is active.
+    if (envp) for (size_t i = 0; envp[i]; ++i) {
+        context->envs.push_back(std::string(envp[i]));
+    }
+
+    for (auto& str : context->args) {
+        context->argv_hold.push_back(str.c_str());
+    }
+    context->argv_hold.push_back(nullptr);
+    for (auto& str : context->envs) {
+        context->envp_hold.push_back(str.c_str());
+    }
+    context->envp_hold.push_back(nullptr);
+
+    context->argc = context->argv_hold.size() - 1;
+    context->argv = (char* const*)&context->argv_hold[0];
+    context->envp = (char* const*)&context->envp_hold[0];
+
+#ifdef DEBUG
+    fprintf(stderr, "argv[%d] = {", context->argc);
+    for (auto str : context->argv_hold) {
+        fprintf(stderr, " \"%s\"", str ?: "nullptr");
+    }
+    fprintf(stderr, " }\n");
+    fflush(stderr);
+#endif
+    context->retval = EXIT_SUCCESS;
+    if (pthread_create(&context->thr, &attr,
+                       (void*(*)(void*))__logcat, context)) {
+        int save_errno = errno;
+        goto argv_exit;
+    }
+    pthread_attr_destroy(&attr);
+
+    return context->fds[0];
+
+argv_exit:
+    context->argv_hold.clear();
+    context->args.clear();
+    context->envp_hold.clear();
+    context->envs.clear();
+pthread_attr_exit:
+    pthread_attr_destroy(&attr);
+close_exit:
+    close(context->fds[0]);
+    context->fds[0] = -1;
+    close(context->fds[1]);
+    context->fds[1] = -1;
+exit:
+    errno = save_errno;
+    context->stop = true;
+    context->thread_stopped = true;
+    context->retval = EXIT_FAILURE;
+    return -1;
+}
+
+// test if the thread is still doing 'stuff'
+int android_logcat_run_command_thread_running(android_logcat_context ctx) {
+    android_logcat_context_internal* context = ctx;
+
+    return context->thread_stopped == false;
+}
+
+// Finished with context
+int android_logcat_destroy(android_logcat_context* ctx) {
+    android_logcat_context_internal* context = *ctx;
+
+    if (!context) return -EBADF;
+
+    *ctx = nullptr;
+
+    context->stop = true;
+
+    while (context->thread_stopped == false) {
+        // Makes me sad, replace thread_stopped with semaphore.  Short lived.
+        sched_yield();
+    }
+
+    delete context->regex;
+    context->argv_hold.clear();
+    context->args.clear();
+    context->envp_hold.clear();
+    context->envs.clear();
+    if (context->fds[0] >= 0) {
+        close(context->fds[0]);
+        context->fds[0] = -1;
+    }
+    android::close_output(context);
+    android::close_error(context);
+    if (context->fds[1] >= 0) {
+        // NB: could be closed by the above fclose(s), ignore error.
+        int save_errno = errno;
+        close(context->fds[1]);
+        errno = save_errno;
+        context->fds[1] = -1;
+    }
+
+    android_closeEventTagMap(context->eventTagMap);
+
+    int retval = context->retval;
+
+    free(context);
+
+    return retval;
 }
diff --git a/logcat/logcat_main.cpp b/logcat/logcat_main.cpp
new file mode 100644
index 0000000..9477e79
--- /dev/null
+++ b/logcat/logcat_main.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 <signal.h>
+#include <stdlib.h>
+
+#include <log/logcat.h>
+
+int main(int argc, char** argv, char** envp) {
+    android_logcat_context ctx = create_android_logcat();
+    if (!ctx) return -1;
+    signal(SIGPIPE, exit);
+    int retval = android_logcat_run_command(ctx, -1, -1, argc, argv, envp);
+    int ret = android_logcat_destroy(&ctx);
+    if (!ret) ret = retval;
+    return ret;
+}
diff --git a/logcat/logcat_system.cpp b/logcat/logcat_system.cpp
new file mode 100644
index 0000000..ea393bd
--- /dev/null
+++ b/logcat/logcat_system.cpp
@@ -0,0 +1,148 @@
+/*
+ * 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 <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <log/logcat.h>
+
+static std::string unquote(const char*& cp, const char*& delim) {
+    if ((*cp == '\'') || (*cp == '"')) {
+        // KISS: Simple quotes. Do not handle the case
+        //       of concatenation like "blah"foo'bar'
+        char quote = *cp++;
+        delim = strchr(cp, quote);
+        if (!delim) delim = cp + strlen(cp);
+        std::string str(cp, delim);
+        if (*delim) ++delim;
+        return str;
+    }
+    delim = strpbrk(cp, " \t\f\r\n");
+    if (!delim) delim = cp + strlen(cp);
+    return std::string(cp, delim);
+}
+
+static bool __android_logcat_parse(const char* command,
+                                   std::vector<std::string>& args,
+                                   std::vector<std::string>& envs) {
+    for (const char *delim, *cp = command; cp && *cp; cp = delim) {
+        while (isspace(*cp)) ++cp;
+        if ((args.size() == 0) && (*cp != '=') && !isdigit(*cp)) {
+            const char* env = cp;
+            while (isalnum(*cp) || (*cp == '_')) ++cp;
+            if (cp && (*cp == '=')) {
+                std::string str(env, ++cp);
+                str += unquote(cp, delim);
+                envs.push_back(str);
+                continue;
+            }
+            cp = env;
+        }
+        args.push_back(unquote(cp, delim));
+        if ((args.size() == 1) && (args[0] != "logcat") &&
+            (args[0] != "/system/bin/logcat")) {
+            return false;
+        }
+    }
+    return args.size() != 0;
+}
+
+FILE* android_logcat_popen(android_logcat_context* ctx, const char* command) {
+    *ctx = NULL;
+
+    std::vector<std::string> args;
+    std::vector<std::string> envs;
+    if (!__android_logcat_parse(command, args, envs)) return NULL;
+
+    std::vector<const char*> argv;
+    for (auto& str : args) {
+        argv.push_back(str.c_str());
+    }
+    argv.push_back(NULL);
+
+    std::vector<const char*> envp;
+    for (auto& str : envs) {
+        envp.push_back(str.c_str());
+    }
+    envp.push_back(NULL);
+
+    *ctx = create_android_logcat();
+    if (!*ctx) return NULL;
+
+    int fd = android_logcat_run_command_thread(
+        *ctx, argv.size() - 1, (char* const*)&argv[0], (char* const*)&envp[0]);
+    argv.clear();
+    args.clear();
+    envp.clear();
+    envs.clear();
+    if (fd < 0) {
+        android_logcat_destroy(ctx);
+        return NULL;
+    }
+
+    FILE* retval = fdopen(fd, "reb");
+    if (!retval) android_logcat_destroy(ctx);
+    return retval;
+}
+
+int android_logcat_pclose(android_logcat_context* ctx, FILE* output) {
+    if (*ctx) {
+        static const useconds_t wait_sample = 20000;
+        // Wait two seconds maximum
+        for (size_t retry = ((2 * 1000000) + wait_sample - 1) / wait_sample;
+             android_logcat_run_command_thread_running(*ctx) && retry; --retry) {
+            usleep(wait_sample);
+        }
+    }
+
+    if (output) fclose(output);
+    return android_logcat_destroy(ctx);
+}
+
+int android_logcat_system(const char* command) {
+    std::vector<std::string> args;
+    std::vector<std::string> envs;
+    if (!__android_logcat_parse(command, args, envs)) return -1;
+
+    std::vector<const char*> argv;
+    for (auto& str : args) {
+        argv.push_back(str.c_str());
+    }
+    argv.push_back(NULL);
+
+    std::vector<const char*> envp;
+    for (auto& str : envs) {
+        envp.push_back(str.c_str());
+    }
+    envp.push_back(NULL);
+
+    android_logcat_context ctx = create_android_logcat();
+    if (!ctx) return -1;
+    /* Command return value */
+    int retval = android_logcat_run_command(ctx, -1, -1, argv.size() - 1,
+                                            (char* const*)&argv[0],
+                                            (char* const*)&envp[0]);
+    /* destroy return value */
+    int ret = android_logcat_destroy(&ctx);
+    /* Paranoia merging any discrepancies between the two return values */
+    if (!ret) ret = retval;
+    return ret;
+}
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index 99c2e0a..22aca17 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -27,11 +27,12 @@
     -fno-builtin \
 
 # -----------------------------------------------------------------------------
-# Benchmarks (actually a gTest where the result code does not matter)
+# Benchmarks
 # ----------------------------------------------------------------------------
 
 benchmark_src_files := \
-    logcat_benchmark.cpp
+    logcat_benchmark.cpp \
+    exec_benchmark.cpp \
 
 # Build benchmarks for the device. Run with:
 #   adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
@@ -40,7 +41,8 @@
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
 LOCAL_SRC_FILES := $(benchmark_src_files)
-include $(BUILD_NATIVE_TEST)
+LOCAL_SHARED_LIBRARIES := libbase liblogcat
+include $(BUILD_NATIVE_BENCHMARK)
 
 # -----------------------------------------------------------------------------
 # Unit tests.
@@ -48,6 +50,7 @@
 
 test_src_files := \
     logcat_test.cpp \
+    liblogcat_test.cpp \
 
 # Build tests for the device (with .so). Run with:
 #   adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
@@ -55,6 +58,6 @@
 LOCAL_MODULE := $(test_module_prefix)unit-tests
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SHARED_LIBRARIES := liblog libbase liblogcat
 LOCAL_SRC_FILES := $(test_src_files)
 include $(BUILD_NATIVE_TEST)
diff --git a/logcat/tests/exec_benchmark.cpp b/logcat/tests/exec_benchmark.cpp
new file mode 100644
index 0000000..c30a5f5
--- /dev/null
+++ b/logcat/tests/exec_benchmark.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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 <stdio.h>
+
+#include <android-base/file.h>
+#include <benchmark/benchmark.h>
+#include <log/logcat.h>
+
+// Dump the statistics and report results
+
+static void logcat_popen_libc(benchmark::State& state, const char* cmd) {
+    while (state.KeepRunning()) {
+        FILE* fp = popen(cmd, "r");
+        std::string ret;
+        android::base::ReadFdToString(fileno(fp), &ret);
+        pclose(fp);
+    }
+}
+
+static void BM_logcat_stat_popen_libc(benchmark::State& state) {
+    logcat_popen_libc(state, "logcat -b all -S");
+}
+BENCHMARK(BM_logcat_stat_popen_libc);
+
+static void logcat_popen_liblogcat(benchmark::State& state, const char* cmd) {
+    while (state.KeepRunning()) {
+        android_logcat_context ctx;
+        FILE* fp = android_logcat_popen(&ctx, cmd);
+        std::string ret;
+        android::base::ReadFdToString(fileno(fp), &ret);
+        android_logcat_pclose(&ctx, fp);
+    }
+}
+
+static void BM_logcat_stat_popen_liblogcat(benchmark::State& state) {
+    logcat_popen_liblogcat(state, "logcat -b all -S");
+}
+BENCHMARK(BM_logcat_stat_popen_liblogcat);
+
+static void logcat_system_libc(benchmark::State& state, const char* cmd) {
+    while (state.KeepRunning()) {
+        system(cmd);
+    }
+}
+
+static void BM_logcat_stat_system_libc(benchmark::State& state) {
+    logcat_system_libc(state, "logcat -b all -S >/dev/null 2>/dev/null");
+}
+BENCHMARK(BM_logcat_stat_system_libc);
+
+static void logcat_system_liblogcat(benchmark::State& state, const char* cmd) {
+    while (state.KeepRunning()) {
+        android_logcat_system(cmd);
+    }
+}
+
+static void BM_logcat_stat_system_liblogcat(benchmark::State& state) {
+    logcat_system_liblogcat(state, "logcat -b all -S >/dev/null 2>/dev/null");
+}
+BENCHMARK(BM_logcat_stat_system_liblogcat);
+
+// Dump the logs and report results
+
+static void BM_logcat_dump_popen_libc(benchmark::State& state) {
+    logcat_popen_libc(state, "logcat -b all -d");
+}
+BENCHMARK(BM_logcat_dump_popen_libc);
+
+static void BM_logcat_dump_popen_liblogcat(benchmark::State& state) {
+    logcat_popen_liblogcat(state, "logcat -b all -d");
+}
+BENCHMARK(BM_logcat_dump_popen_liblogcat);
+
+static void BM_logcat_dump_system_libc(benchmark::State& state) {
+    logcat_system_libc(state, "logcat -b all -d >/dev/null 2>/dev/null");
+}
+BENCHMARK(BM_logcat_dump_system_libc);
+
+static void BM_logcat_dump_system_liblogcat(benchmark::State& state) {
+    logcat_system_liblogcat(state, "logcat -b all -d >/dev/null 2>/dev/null");
+}
+BENCHMARK(BM_logcat_dump_system_liblogcat);
diff --git a/logcat/tests/liblogcat_test.cpp b/logcat/tests/liblogcat_test.cpp
new file mode 100644
index 0000000..9e9a2c2
--- /dev/null
+++ b/logcat/tests/liblogcat_test.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <log/logcat.h>
+
+#define logcat_define(context) android_logcat_context context
+#define logcat_popen(context, command) android_logcat_popen(&context, command)
+#define logcat_pclose(context, fp) android_logcat_pclose(&context, fp)
+#define logcat_system(command) android_logcat_system(command)
+#define logcat liblogcat
+
+#include "logcat_test.cpp"
diff --git a/logcat/tests/logcat_benchmark.cpp b/logcat/tests/logcat_benchmark.cpp
index dd85164..8d88628 100644
--- a/logcat/tests/logcat_benchmark.cpp
+++ b/logcat/tests/logcat_benchmark.cpp
@@ -18,19 +18,22 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <gtest/gtest.h>
+#include <benchmark/benchmark.h>
 
 static const char begin[] = "--------- beginning of ";
 
-TEST(logcat, sorted_order) {
-    FILE *fp;
+static void BM_logcat_sorted_order(benchmark::State& state) {
+    FILE* fp;
 
-    ASSERT_TRUE(NULL != (fp = popen(
-      "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null",
-      "r")));
+    if (!state.KeepRunning()) return;
+
+    fp = popen(
+        "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null",
+        "r");
+    if (!fp) return;
 
     class timestamp {
-    private:
+       private:
         int month;
         int day;
         int hour;
@@ -39,44 +42,39 @@
         int millisecond;
         bool ok;
 
-    public:
-        void init(const char *buffer)
-        {
+       public:
+        void init(const char* buffer) {
             ok = false;
             if (buffer != NULL) {
-                ok = sscanf(buffer, "%d-%d %d:%d:%d.%d ",
-                    &month, &day, &hour, &minute, &second, &millisecond) == 6;
+                ok = sscanf(buffer, "%d-%d %d:%d:%d.%d ", &month, &day, &hour,
+                            &minute, &second, &millisecond) == 6;
             }
         }
 
-        explicit timestamp(const char *buffer)
-        {
+        explicit timestamp(const char* buffer) {
             init(buffer);
         }
 
-        bool operator< (timestamp &T)
-        {
-            return !ok || !T.ok
-             || (month < T.month)
-             || ((month == T.month)
-              && ((day < T.day)
-               || ((day == T.day)
-                && ((hour < T.hour)
-                 || ((hour == T.hour)
-                  && ((minute < T.minute)
-                   || ((minute == T.minute)
-                    && ((second < T.second)
-                     || ((second == T.second)
-                      && (millisecond < T.millisecond))))))))));
+        bool operator<(timestamp& T) {
+            return !ok || !T.ok || (month < T.month) ||
+                   ((month == T.month) &&
+                    ((day < T.day) ||
+                     ((day == T.day) &&
+                      ((hour < T.hour) ||
+                       ((hour == T.hour) &&
+                        ((minute < T.minute) ||
+                         ((minute == T.minute) &&
+                          ((second < T.second) ||
+                           ((second == T.second) &&
+                            (millisecond < T.millisecond))))))))));
         }
 
-        bool valid(void)
-        {
+        bool valid(void) {
             return ok;
         }
     } last(NULL);
 
-    char *last_buffer = NULL;
+    char* last_buffer = NULL;
     char buffer[5120];
 
     int count = 0;
@@ -114,15 +112,22 @@
 
     // Allow few fails, happens with readers active
     fprintf(stderr, "%s: %d/%d out of order entries\n",
-            (next_lt_last)
-                ? ((next_lt_last <= max_ok)
-                    ? "WARNING"
-                    : "ERROR")
-                : "INFO",
+            (next_lt_last) ? ((next_lt_last <= max_ok) ? "WARNING" : "ERROR")
+                           : "INFO",
             next_lt_last, count);
 
-    EXPECT_GE(max_ok, next_lt_last);
+    if (next_lt_last > max_ok) {
+        fprintf(stderr, "EXPECT_GE(max_ok=%d, next_lt_last=%d)\n", max_ok,
+                next_lt_last);
+    }
 
     // sample statistically too small
-    EXPECT_LT(100, count);
+    if (count < 100) {
+        fprintf(stderr, "EXPECT_LT(100, count=%d)\n", count);
+    }
+
+    state.KeepRunning();
 }
+BENCHMARK(BM_logcat_sorted_order);
+
+BENCHMARK_MAIN();
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 11cffe6..9c777b3 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -23,39 +23,55 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 #include <memory>
 #include <string>
 
+#include <android-base/file.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
 #include <log/log_event_list.h>
 
+#ifndef logcat_popen
+#define logcat_define(context)
+#define logcat_popen(context, command) popen((command), "r")
+#define logcat_pclose(context, fp) pclose(fp)
+#define logcat_system(command) system(command)
+#endif
+
 #define BIG_BUFFER (5 * 1024)
 
 // 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
 // than try a usleep.
-#define LOG_FAILURE_RETRY(exp) ({  \
-    typeof (exp) _rc;              \
-    do {                           \
-        _rc = (exp);               \
-    } while (((_rc == -1)          \
-           && ((errno == EINTR)    \
-            || (errno == EAGAIN))) \
-          || (_rc == -EINTR)       \
-          || (_rc == -EAGAIN));    \
-    _rc; })
+#define LOG_FAILURE_RETRY(exp)                                               \
+    ({                                                                       \
+        typeof(exp) _rc;                                                     \
+        do {                                                                 \
+            _rc = (exp);                                                     \
+        } while (((_rc == -1) && ((errno == EINTR) || (errno == EAGAIN))) || \
+                 (_rc == -EINTR) || (_rc == -EAGAIN));                       \
+        _rc;                                                                 \
+    })
 
 static const char begin[] = "--------- beginning of ";
 
 TEST(logcat, buckets) {
-    FILE *fp;
+    FILE* fp;
+    logcat_define(ctx);
 
-    ASSERT_TRUE(NULL != (fp = popen(
-      "logcat -b radio -b events -b system -b main -d 2>/dev/null",
-      "r")));
+#undef LOG_TAG
+#define LOG_TAG "inject"
+    RLOGE("logcat.buckets");
+    sleep(1);
+
+    ASSERT_TRUE(
+        NULL !=
+        (fp = logcat_popen(
+             ctx,
+             "logcat -b radio -b events -b system -b main -d 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -64,7 +80,7 @@
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
-            while (char *cp = strrchr(buffer, '\n')) {
+            while (char* cp = strrchr(buffer, '\n')) {
                 *cp = '\0';
             }
             log_id_t id = android_name_to_log_id(buffer + sizeof(begin) - 1);
@@ -73,7 +89,7 @@
         }
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     EXPECT_EQ(15, ids);
 
@@ -81,11 +97,14 @@
 }
 
 TEST(logcat, event_tag_filter) {
-    FILE *fp;
+    FILE* fp;
+    logcat_define(ctx);
 
-    ASSERT_TRUE(NULL != (fp = popen(
-      "logcat -b events -d -s auditd am_proc_start am_pss am_proc_bound dvm_lock_sample am_wtf 2>/dev/null",
-      "r")));
+    ASSERT_TRUE(NULL !=
+                (fp = logcat_popen(ctx,
+                                   "logcat -b events -d -s auditd "
+                                   "am_proc_start am_pss am_proc_bound "
+                                   "dvm_lock_sample am_wtf 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -95,67 +114,99 @@
         ++count;
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     EXPECT_LT(4, count);
 }
 
-TEST(logcat, year) {
+// If there is not enough background noise in the logs, then spam the logs to
+// permit tail checking so that the tests can progress.
+static size_t inject(ssize_t count) {
+    if (count <= 0) return 0;
 
+    static const size_t retry = 4;
+    size_t errors = retry;
+    size_t num = 0;
+    for (;;) {
+        log_time ts(CLOCK_MONOTONIC);
+        if (__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) >= 0) {
+            if (++num >= (size_t)count) {
+                // let data settle end-to-end
+                sleep(3);
+                return num;
+            }
+            errors = retry;
+            usleep(100);  // ~32 per timer tick, we are a spammer regardless
+        } else if (--errors <= 0) {
+            return num;
+        }
+    }
+    // NOTREACH
+    return num;
+}
+
+TEST(logcat, year) {
     if (android_log_clockid() == CLOCK_MONOTONIC) {
         fprintf(stderr, "Skipping test, logd is monotonic time\n");
         return;
     }
 
-    FILE *fp;
+    int count;
+    int tries = 3;  // in case run too soon after system start or buffer clear
 
-    char needle[32];
-    time_t now;
-    time(&now);
-    struct tm *ptm;
+    do {
+        FILE* fp;
+        logcat_define(ctx);
+
+        char needle[32];
+        time_t now;
+        time(&now);
+        struct tm* ptm;
 #if !defined(_WIN32)
-    struct tm tmBuf;
-    ptm = localtime_r(&now, &tmBuf);
+        struct tm tmBuf;
+        ptm = localtime_r(&now, &tmBuf);
 #else
-    ptm = localtime(&&now);
+        ptm = localtime(&&now);
 #endif
-    strftime(needle, sizeof(needle), "[ %Y-", ptm);
+        strftime(needle, sizeof(needle), "[ %Y-", ptm);
 
-    ASSERT_TRUE(NULL != (fp = popen(
-      "logcat -v long -v year -b all -t 3 2>/dev/null",
-      "r")));
+        ASSERT_TRUE(
+            NULL !=
+            (fp = logcat_popen(
+                 ctx, "logcat -v long -v year -b all -t 3 2>/dev/null")));
 
-    char buffer[BIG_BUFFER];
+        char buffer[BIG_BUFFER];
 
-    int count = 0;
+        count = 0;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if (!strncmp(buffer, needle, strlen(needle))) {
-            ++count;
+        while (fgets(buffer, sizeof(buffer), fp)) {
+            if (!strncmp(buffer, needle, strlen(needle))) {
+                ++count;
+            }
         }
-    }
+        logcat_pclose(ctx, fp);
 
-    pclose(fp);
+    } while ((count < 3) && --tries && inject(3 - count));
 
     ASSERT_EQ(3, count);
 }
 
 // Return a pointer to each null terminated -v long time field.
-char *fgetLongTime(char *buffer, size_t buflen, FILE *fp) {
+static char* fgetLongTime(char* buffer, size_t buflen, FILE* fp) {
     while (fgets(buffer, buflen, fp)) {
-        char *cp = buffer;
+        char* cp = buffer;
         if (*cp != '[') {
             continue;
         }
         while (*++cp == ' ') {
             ;
         }
-        char *ep = cp;
+        char* ep = cp;
         while (isdigit(*ep)) {
             ++ep;
         }
         if ((*ep != '-') && (*ep != '.')) {
-           continue;
+            continue;
         }
         // Find PID field
         while (((ep = strchr(ep, ':'))) && (*++ep != ' ')) {
@@ -164,7 +215,8 @@
         if (!ep) {
             continue;
         }
-        ep -= 7;
+        static const size_t pid_field_width = 7;
+        ep -= pid_field_width;
         *ep = '\0';
         return cp;
     }
@@ -172,21 +224,22 @@
 }
 
 TEST(logcat, tz) {
-
     if (android_log_clockid() == CLOCK_MONOTONIC) {
         fprintf(stderr, "Skipping test, logd is monotonic time\n");
         return;
     }
 
-    int tries = 3; // in case run too soon after system start or buffer clear
+    int tries = 4;  // in case run too soon after system start or buffer clear
     int count;
 
     do {
-        FILE *fp;
+        FILE* fp;
+        logcat_define(ctx);
 
-        ASSERT_TRUE(NULL != (fp = popen(
-          "logcat -v long -v America/Los_Angeles -b all -t 3 2>/dev/null",
-          "r")));
+        ASSERT_TRUE(NULL !=
+                    (fp = logcat_popen(ctx,
+                                       "logcat -v long -v America/Los_Angeles "
+                                       "-b all -t 3 2>/dev/null")));
 
         char buffer[BIG_BUFFER];
 
@@ -195,22 +248,26 @@
         while (fgetLongTime(buffer, sizeof(buffer), fp)) {
             if (strstr(buffer, " -0700") || strstr(buffer, " -0800")) {
                 ++count;
+            } else {
+                fprintf(stderr, "ts=\"%s\"\n", buffer + 2);
             }
         }
 
-        pclose(fp);
+        logcat_pclose(ctx, fp);
 
-    } while ((count < 3) && --tries && (sleep(1), true));
+    } while ((count < 3) && --tries && inject(3 - count));
 
     ASSERT_EQ(3, count);
 }
 
 TEST(logcat, ntz) {
-    FILE *fp;
+    FILE* fp;
+    logcat_define(ctx);
 
-    ASSERT_TRUE(NULL != (fp = popen(
-      "logcat -v long -v America/Los_Angeles -v zone -b all -t 3 2>/dev/null",
-      "r")));
+    ASSERT_TRUE(NULL !=
+                (fp = logcat_popen(ctx,
+                                   "logcat -v long -v America/Los_Angeles -v "
+                                   "zone -b all -t 3 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -222,24 +279,26 @@
         }
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     ASSERT_EQ(0, count);
 }
 
-void do_tail(int num) {
-    int tries = 3; // in case run too soon after system start or buffer clear
+static void do_tail(int num) {
+    int tries = 4;  // in case run too soon after system start or buffer clear
     int count;
 
+    if (num > 10) ++tries;
+    if (num > 100) ++tries;
     do {
         char buffer[BIG_BUFFER];
 
         snprintf(buffer, sizeof(buffer),
-          "logcat -v long -b radio -b events -b system -b main -t %d 2>/dev/null",
-          num);
+                 "ANDROID_PRINTF_LOG=long logcat -b all -t %d 2>/dev/null", num);
 
-        FILE *fp;
-        ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+        FILE* fp;
+        logcat_define(ctx);
+        ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
 
         count = 0;
 
@@ -247,9 +306,9 @@
             ++count;
         }
 
-        pclose(fp);
+        logcat_pclose(ctx, fp);
 
-    } while ((count < num) && --tries && (sleep(1), true));
+    } while ((count < num) && --tries && inject(num - count));
 
     ASSERT_EQ(num, count);
 }
@@ -271,57 +330,118 @@
 }
 
 TEST(logcat, tail_time) {
-    FILE *fp;
-
-    ASSERT_TRUE(NULL != (fp = popen("logcat -v long -b all -t 10 2>&1", "r")));
-
+    FILE* fp;
+    int count;
     char buffer[BIG_BUFFER];
-    char *last_timestamp = NULL;
-    char *first_timestamp = NULL;
-    int count = 0;
+    char* last_timestamp = NULL;
+    // Hard to predict 100% if first (overlap) or second line will match.
+    // -v nsec will in a substantial majority be the second line.
+    char* first_timestamp = NULL;
+    char* second_timestamp = NULL;
+    char* input;
 
-    char *cp;
-    while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) {
-        ++count;
-        if (!first_timestamp) {
-            first_timestamp = strdup(cp);
+    int tries = 4;  // in case run too soon after system start or buffer clear
+
+    do {
+        logcat_define(ctx);
+        ASSERT_TRUE(NULL != (fp = logcat_popen(ctx,
+                                               "logcat"
+                                               " -v long"
+                                               " -v nsec"
+                                               " -b all"
+                                               " -t 10"
+                                               " 2>&1")));
+        count = 0;
+
+        while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
+            ++count;
+            if (!first_timestamp) {
+                first_timestamp = strdup(input);
+            } else if (!second_timestamp) {
+                second_timestamp = strdup(input);
+            }
+            free(last_timestamp);
+            last_timestamp = strdup(input);
         }
-        free(last_timestamp);
-        last_timestamp = strdup(cp);
-    }
-    pclose(fp);
+        logcat_pclose(ctx, fp);
 
-    EXPECT_EQ(10, count);
+    } while ((count < 10) && --tries && inject(10 - count));
+
+    EXPECT_EQ(10, count);  // We want _some_ history, too small, falses below
     EXPECT_TRUE(last_timestamp != NULL);
     EXPECT_TRUE(first_timestamp != NULL);
+    EXPECT_TRUE(second_timestamp != NULL);
 
-    snprintf(buffer, sizeof(buffer), "logcat -v long -b all -t '%s' 2>&1",
+    snprintf(buffer, sizeof(buffer),
+             "logcat"
+             " -v long"
+             " -v nsec"
+             " -b all"
+             " -t '%s'"
+             " 2>&1",
              first_timestamp);
-    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+    logcat_define(ctx);
+    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
 
     int second_count = 0;
     int last_timestamp_count = -1;
 
-    while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) {
+    --count;  // One less unless we match the first_timestamp
+    bool found = false;
+    while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
         ++second_count;
+        // We want to highlight if we skip to the next entry.
+        // WAI, if the time in logd is *exactly*
+        // XX-XX XX:XX:XX.XXXXXX000 (usec) or XX-XX XX:XX:XX.XXX000000
+        // this can happen, but it should not happen with nsec.
+        // We can make this WAI behavior happen 1000 times less
+        // frequently if the caller does not use the -v usec flag,
+        // but always the second (always skip) if they use the
+        // (undocumented) -v nsec flag.
         if (first_timestamp) {
-            // we can get a transitory *extremely* rare failure if hidden
-            // underneath the time is *exactly* XX-XX XX:XX:XX.XXX000000
-            EXPECT_STREQ(cp, first_timestamp);
+            found = !strcmp(input, first_timestamp);
+            if (found) {
+                ++count;
+                GTEST_LOG_(INFO)
+                    << "input = first(" << first_timestamp << ")\n";
+            }
             free(first_timestamp);
             first_timestamp = NULL;
         }
-        if (!strcmp(cp, last_timestamp)) {
+        if (second_timestamp) {
+            found = found || !strcmp(input, second_timestamp);
+            if (!found) {
+                GTEST_LOG_(INFO) << "input(" << input << ") != second("
+                                 << second_timestamp << ")\n";
+            }
+            free(second_timestamp);
+            second_timestamp = NULL;
+        }
+        if (!strcmp(input, last_timestamp)) {
             last_timestamp_count = second_count;
         }
     }
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
+    EXPECT_TRUE(found);
+    if (!found) {
+        if (first_timestamp) {
+            GTEST_LOG_(INFO) << "first = " << first_timestamp << "\n";
+        }
+        if (second_timestamp) {
+            GTEST_LOG_(INFO) << "second = " << second_timestamp << "\n";
+        }
+        if (last_timestamp) {
+            GTEST_LOG_(INFO) << "last = " << last_timestamp << "\n";
+        }
+    }
     free(last_timestamp);
     last_timestamp = NULL;
     free(first_timestamp);
+    free(second_timestamp);
 
     EXPECT_TRUE(first_timestamp == NULL);
+    EXPECT_TRUE(second_timestamp == NULL);
     EXPECT_LE(count, second_count);
     EXPECT_LE(count, last_timestamp_count);
 }
@@ -333,10 +453,11 @@
 
     ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
 
-    FILE *fp;
-    ASSERT_TRUE(NULL != (fp = popen(
-      "logcat -v brief -b events -t 100 2>/dev/null",
-      "r")));
+    FILE* fp;
+    logcat_define(ctx);
+    ASSERT_TRUE(NULL !=
+                (fp = logcat_popen(
+                     ctx, "logcat -v brief -b events -t 100 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -346,27 +467,28 @@
         int p;
         unsigned long long t;
 
-        if ((2 != sscanf(buffer, "I/[0]     ( %d): %llu", &p, &t))
-         || (p != pid)) {
+        if ((2 != sscanf(buffer, "I/[0]     ( %d): %llu", &p, &t)) ||
+            (p != pid)) {
             continue;
         }
 
-        log_time tx((const char *) &t);
+        log_time tx((const char*)&t);
         if (ts == tx) {
             ++count;
         }
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     ASSERT_EQ(1, count);
 }
 
-int get_groups(const char *cmd) {
-    FILE *fp;
+static int get_groups(const char* cmd) {
+    FILE* fp;
+    logcat_define(ctx);
 
     // NB: crash log only available in user space
-    EXPECT_TRUE(NULL != (fp = popen(cmd, "r")));
+    EXPECT_TRUE(NULL != (fp = logcat_popen(ctx, cmd)));
 
     if (fp == NULL) {
         return 0;
@@ -384,84 +506,88 @@
         size = consumed = max = payload = 0;
         // NB: crash log can be very small, not hit a Kb of consumed space
         //     doubly lucky we are not including it.
-        if (6 != sscanf(buffer, "%*s ring buffer is %d%2s (%d%2s consumed),"
-                                " max entry is %db, max payload is %db",
-                                &size, size_mult, &consumed, consumed_mult,
-                                &max, &payload)) {
+        if (6 != sscanf(buffer,
+                        "%*s ring buffer is %d%2s (%d%2s consumed),"
+                        " max entry is %db, max payload is %db",
+                        &size, size_mult, &consumed, consumed_mult, &max,
+                        &payload)) {
             fprintf(stderr, "WARNING: Parse error: %s", buffer);
             continue;
         }
         full_size = size;
-        switch(size_mult[0]) {
-        case 'G':
-            full_size *= 1024;
+        switch (size_mult[0]) {
+            case 'G':
+                full_size *= 1024;
             /* FALLTHRU */
-        case 'M':
-            full_size *= 1024;
+            case 'M':
+                full_size *= 1024;
             /* FALLTHRU */
-        case 'K':
-            full_size *= 1024;
+            case 'K':
+                full_size *= 1024;
             /* FALLTHRU */
-        case 'b':
-            break;
+            case 'b':
+                break;
         }
         full_consumed = consumed;
-        switch(consumed_mult[0]) {
-        case 'G':
-            full_consumed *= 1024;
+        switch (consumed_mult[0]) {
+            case 'G':
+                full_consumed *= 1024;
             /* FALLTHRU */
-        case 'M':
-            full_consumed *= 1024;
+            case 'M':
+                full_consumed *= 1024;
             /* FALLTHRU */
-        case 'K':
-            full_consumed *= 1024;
+            case 'K':
+                full_consumed *= 1024;
             /* FALLTHRU */
-        case 'b':
-            break;
+            case 'b':
+                break;
         }
         EXPECT_GT((full_size * 9) / 4, full_consumed);
         EXPECT_GT(full_size, max);
         EXPECT_GT(max, payload);
 
-        if ((((full_size * 9) / 4) >= full_consumed)
-         && (full_size > max)
-         && (max > payload)) {
+        if ((((full_size * 9) / 4) >= full_consumed) && (full_size > max) &&
+            (max > payload)) {
             ++count;
         }
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     return count;
 }
 
 TEST(logcat, get_size) {
-    ASSERT_EQ(4, get_groups(
-      "logcat -v brief -b radio -b events -b system -b main -g 2>/dev/null"));
+    ASSERT_EQ(4, get_groups("logcat -v brief -b radio -b events -b system -b "
+                            "main -g 2>/dev/null"));
 }
 
 // duplicate test for get_size, but use comma-separated list of buffers
 TEST(logcat, multiple_buffer) {
-    ASSERT_EQ(4, get_groups(
-      "logcat -v brief -b radio,events,system,main -g 2>/dev/null"));
+    ASSERT_EQ(
+        4, get_groups(
+               "logcat -v brief -b radio,events,system,main -g 2>/dev/null"));
 }
 
 TEST(logcat, bad_buffer) {
-    ASSERT_EQ(0, get_groups(
-      "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
+    ASSERT_EQ(
+        0,
+        get_groups(
+            "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
 }
 
-static void caught_blocking(int /*signum*/)
-{
+#ifndef logcat
+static void caught_blocking(int signum) {
     unsigned long long v = 0xDEADBEEFA55A0000ULL;
 
     v += getpid() & 0xFFFF;
+    if (signum == 0) ++v;
 
     LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
 }
 
 TEST(logcat, blocking) {
-    FILE *fp;
+    FILE* fp;
     unsigned long long v = 0xDEADBEEFA55F0000ULL;
 
     pid_t pid = getpid();
@@ -472,10 +598,11 @@
 
     v &= 0xFFFFFFFFFFFAFFFFULL;
 
-    ASSERT_TRUE(NULL != (fp = popen(
-      "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
-      " logcat -v brief -b events 2>&1",
-      "r")));
+    ASSERT_TRUE(
+        NULL !=
+        (fp = popen("( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
+                    " logcat -v brief -b events 2>&1",
+                    "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -486,7 +613,6 @@
     signal(SIGALRM, caught_blocking);
     alarm(2);
     while (fgets(buffer, sizeof(buffer), fp)) {
-
         if (!strncmp(buffer, "DONE", 4)) {
             break;
         }
@@ -496,8 +622,7 @@
         int p;
         unsigned long long l;
 
-        if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l))
-         || (p != pid)) {
+        if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) || (p != pid)) {
             continue;
         }
 
@@ -520,17 +645,17 @@
     EXPECT_EQ(1, signals);
 }
 
-static void caught_blocking_tail(int /*signum*/)
-{
+static void caught_blocking_tail(int signum) {
     unsigned long long v = 0xA55ADEADBEEF0000ULL;
 
     v += getpid() & 0xFFFF;
+    if (signum == 0) ++v;
 
     LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
 }
 
 TEST(logcat, blocking_tail) {
-    FILE *fp;
+    FILE* fp;
     unsigned long long v = 0xA55FDEADBEEF0000ULL;
 
     pid_t pid = getpid();
@@ -541,10 +666,11 @@
 
     v &= 0xFFFAFFFFFFFFFFFFULL;
 
-    ASSERT_TRUE(NULL != (fp = popen(
-      "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
-      " logcat -v brief -b events -T 5 2>&1",
-      "r")));
+    ASSERT_TRUE(
+        NULL !=
+        (fp = popen("( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
+                    " logcat -v brief -b events -T 5 2>&1",
+                    "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -555,7 +681,6 @@
     signal(SIGALRM, caught_blocking_tail);
     alarm(2);
     while (fgets(buffer, sizeof(buffer), fp)) {
-
         if (!strncmp(buffer, "DONE", 4)) {
             break;
         }
@@ -565,8 +690,7 @@
         int p;
         unsigned long long l;
 
-        if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l))
-         || (p != pid)) {
+        if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) || (p != pid)) {
             continue;
         }
 
@@ -590,13 +714,13 @@
 
     EXPECT_EQ(1, signals);
 }
+#endif
 
 // meant to be handed to ASSERT_FALSE / EXPECT_FALSE to expand the message
 static testing::AssertionResult IsFalse(int ret, const char* command) {
-    return ret ?
-        (testing::AssertionSuccess() <<
-            "ret=" << ret << " command=\"" << command << "\"") :
-        testing::AssertionFailure();
+    return ret ? (testing::AssertionSuccess()
+                  << "ret=" << ret << " command=\"" << command << "\"")
+               : testing::AssertionFailure();
 }
 
 TEST(logcat, logrotate) {
@@ -604,17 +728,18 @@
     char buf[sizeof(form)];
     ASSERT_TRUE(NULL != mkdtemp(strcpy(buf, form)));
 
-    static const char comm[] = "logcat -b radio -b events -b system -b main"
-                                     " -d -f %s/log.txt -n 7 -r 1";
+    static const char comm[] =
+        "logcat -b radio -b events -b system -b main"
+        " -d -f %s/log.txt -n 7 -r 1";
     char command[sizeof(buf) + sizeof(comm)];
     snprintf(command, sizeof(command), comm, buf);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = system(command), command));
+    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
     if (!ret) {
         snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf);
 
-        FILE *fp;
+        FILE* fp;
         EXPECT_TRUE(NULL != (fp = popen(command, "r")));
         if (fp) {
             char buffer[BIG_BUFFER];
@@ -626,7 +751,7 @@
                 char c;
 
                 if ((2 == sscanf(buffer, "%d log.tx%c", &num, &c)) &&
-                        (num <= 40)) {
+                    (num <= 40)) {
                     ++count;
                 } else if (strncmp(buffer, total, sizeof(total) - 1)) {
                     fprintf(stderr, "WARNING: Parse error: %s", buffer);
@@ -644,21 +769,23 @@
 }
 
 TEST(logcat, logrotate_suffix) {
-    static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+    static const char tmp_out_dir_form[] =
+        "/data/local/tmp/logcat.logrotate.XXXXXX";
     char tmp_out_dir[sizeof(tmp_out_dir_form)];
     ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
 
-    static const char logcat_cmd[] = "logcat -b radio -b events -b system -b main"
-                                     " -d -f %s/log.txt -n 10 -r 1";
+    static const char logcat_cmd[] =
+        "logcat -b radio -b events -b system -b main"
+        " -d -f %s/log.txt -n 10 -r 1";
     char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd)];
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = system(command), command));
+    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
     if (!ret) {
         snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir);
 
-        FILE *fp;
+        FILE* fp;
         EXPECT_TRUE(NULL != (fp = popen(command, "r")));
         char buffer[BIG_BUFFER];
         int log_file_count = 0;
@@ -669,21 +796,24 @@
                 strlen(rotated_log_filename_prefix);
             static const char log_filename[] = "log.txt";
 
-            if (!strncmp(buffer, rotated_log_filename_prefix, rotated_log_filename_prefix_len)) {
-              // Rotated file should have form log.txt.##
-              char* rotated_log_filename_suffix = buffer + rotated_log_filename_prefix_len;
-              char* endptr;
-              const long int suffix_value = strtol(rotated_log_filename_suffix, &endptr, 10);
-              EXPECT_EQ(rotated_log_filename_suffix + 2, endptr);
-              EXPECT_LE(suffix_value, 10);
-              EXPECT_GT(suffix_value, 0);
-              ++log_file_count;
-              continue;
+            if (!strncmp(buffer, rotated_log_filename_prefix,
+                         rotated_log_filename_prefix_len)) {
+                // Rotated file should have form log.txt.##
+                char* rotated_log_filename_suffix =
+                    buffer + rotated_log_filename_prefix_len;
+                char* endptr;
+                const long int suffix_value =
+                    strtol(rotated_log_filename_suffix, &endptr, 10);
+                EXPECT_EQ(rotated_log_filename_suffix + 2, endptr);
+                EXPECT_LE(suffix_value, 10);
+                EXPECT_GT(suffix_value, 0);
+                ++log_file_count;
+                continue;
             }
 
             if (!strncmp(buffer, log_filename, strlen(log_filename))) {
-              ++log_file_count;
-              continue;
+                ++log_file_count;
+                continue;
             }
 
             fprintf(stderr, "ERROR: Unexpected file: %s", buffer);
@@ -697,24 +827,26 @@
 }
 
 TEST(logcat, logrotate_continue) {
-    static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+    static const char tmp_out_dir_form[] =
+        "/data/local/tmp/logcat.logrotate.XXXXXX";
     char tmp_out_dir[sizeof(tmp_out_dir_form)];
     ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
 
     static const char log_filename[] = "log.txt";
-    static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n 256 -r 1024";
+    static const char logcat_cmd[] =
+        "logcat -b all -v nsec -d -f %s/%s -n 256 -r 1024";
     static const char cleanup_cmd[] = "rm -rf %s";
     char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename)];
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = system(command), command));
+    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
     if (ret) {
         snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
         EXPECT_FALSE(IsFalse(system(command), command));
         return;
     }
-    FILE *fp;
+    FILE* fp;
     snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, log_filename);
     EXPECT_TRUE(NULL != ((fp = fopen(command, "r"))));
     if (!fp) {
@@ -722,11 +854,18 @@
         EXPECT_FALSE(IsFalse(system(command), command));
         return;
     }
-    char *line = NULL;
-    char *last_line = NULL; // this line is allowed to stutter, one-line overlap
-    char *second_last_line = NULL;
+    char* line = NULL;
+    char* last_line =
+        NULL;  // this line is allowed to stutter, one-line overlap
+    char* second_last_line = NULL;  // should never stutter
+    char* first_line = NULL;        // help diagnose failure?
     size_t len = 0;
     while (getline(&line, &len, fp) != -1) {
+        if (!first_line) {
+            first_line = line;
+            line = NULL;
+            continue;
+        }
         free(second_last_line);
         second_last_line = last_line;
         last_line = line;
@@ -744,25 +883,31 @@
     if (!second_last_line) {
         snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
         EXPECT_FALSE(IsFalse(system(command), command));
+        free(first_line);
         return;
     }
     // re-run the command, it should only add a few lines more content if it
     // continues where it left off.
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
-    EXPECT_FALSE(IsFalse(ret = system(command), command));
+    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
     if (ret) {
         snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
         EXPECT_FALSE(IsFalse(system(command), command));
+        free(second_last_line);
+        free(first_line);
         return;
     }
-    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir),
+                                                  closedir);
     EXPECT_NE(nullptr, dir);
     if (!dir) {
         snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
         EXPECT_FALSE(IsFalse(system(command), command));
+        free(second_last_line);
+        free(first_line);
         return;
     }
-    struct dirent *entry;
+    struct dirent* entry;
     unsigned count = 0;
     while ((entry = readdir(dir.get()))) {
         if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
@@ -788,21 +933,27 @@
         unlink(command);
     }
     if (count > 1) {
-        char *brk = strpbrk(second_last_line, "\r\n");
-        if (!brk) {
-            brk = second_last_line + strlen(second_last_line);
+        char* brk = strpbrk(second_last_line, "\r\n");
+        if (!brk) brk = second_last_line + strlen(second_last_line);
+        fprintf(stderr, "\"%.*s\" occurred %u times\n",
+                (int)(brk - second_last_line), second_last_line, count);
+        if (first_line) {
+            brk = strpbrk(first_line, "\r\n");
+            if (!brk) brk = first_line + strlen(first_line);
+            fprintf(stderr, "\"%.*s\" was first line, fault?\n",
+                    (int)(brk - first_line), first_line);
         }
-        fprintf(stderr, "\"%.*s\" occured %u times\n",
-            (int)(brk - second_last_line), second_last_line, count);
     }
     free(second_last_line);
+    free(first_line);
 
     snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
     EXPECT_FALSE(IsFalse(system(command), command));
 }
 
 TEST(logcat, logrotate_clear) {
-    static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+    static const char tmp_out_dir_form[] =
+        "/data/local/tmp/logcat.logrotate.XXXXXX";
     char tmp_out_dir[sizeof(tmp_out_dir_form)];
     ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
 
@@ -811,28 +962,30 @@
     static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n %d -r 1";
     static const char clear_cmd[] = " -c";
     static const char cleanup_cmd[] = "rm -rf %s";
-    char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename) + sizeof(clear_cmd) + 32];
+    char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) +
+                 sizeof(log_filename) + sizeof(clear_cmd) + 32];
 
     // Run command with all data
     {
-        snprintf(command, sizeof(command) - sizeof(clear_cmd),
-                 logcat_cmd, tmp_out_dir, log_filename, num_val);
+        snprintf(command, sizeof(command) - sizeof(clear_cmd), logcat_cmd,
+                 tmp_out_dir, log_filename, num_val);
 
         int ret;
-        EXPECT_FALSE(IsFalse(ret = system(command), command));
+        EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
         if (ret) {
             snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
             EXPECT_FALSE(IsFalse(system(command), command));
             return;
         }
-        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir),
+                                                      closedir);
         EXPECT_NE(nullptr, dir);
         if (!dir) {
             snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
             EXPECT_FALSE(IsFalse(system(command), command));
             return;
         }
-        struct dirent *entry;
+        struct dirent* entry;
         unsigned count = 0;
         while ((entry = readdir(dir.get()))) {
             if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
@@ -848,21 +1001,22 @@
         strcat(command, clear_cmd);
 
         int ret;
-        EXPECT_FALSE(IsFalse(ret = system(command), command));
+        EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
         if (ret) {
             snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
             EXPECT_FALSE(system(command));
             EXPECT_FALSE(IsFalse(system(command), command));
             return;
         }
-        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir),
+                                                      closedir);
         EXPECT_NE(nullptr, dir);
         if (!dir) {
             snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
             EXPECT_FALSE(IsFalse(system(command), command));
             return;
         }
-        struct dirent *entry;
+        struct dirent* entry;
         unsigned count = 0;
         while ((entry = readdir(dir.get()))) {
             if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
@@ -878,24 +1032,25 @@
     EXPECT_FALSE(IsFalse(system(command), command));
 }
 
-static int logrotate_count_id(const char *logcat_cmd, const char *tmp_out_dir) {
-
+static int logrotate_count_id(const char* logcat_cmd, const char* tmp_out_dir) {
     static const char log_filename[] = "log.txt";
-    char command[strlen(tmp_out_dir) + strlen(logcat_cmd) + strlen(log_filename) + 32];
+    char command[strlen(tmp_out_dir) + strlen(logcat_cmd) +
+                 strlen(log_filename) + 32];
 
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
 
-    int ret;
-    EXPECT_FALSE(IsFalse(ret = system(command), command));
+    int ret = logcat_system(command);
     if (ret) {
+        fprintf(stderr, "system(\"%s\")=%d", command, ret);
         return -1;
     }
-    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
-    EXPECT_NE(nullptr, dir);
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir),
+                                                  closedir);
     if (!dir) {
+        fprintf(stderr, "opendir(\"%s\") failed", tmp_out_dir);
         return -1;
     }
-    struct dirent *entry;
+    struct dirent* entry;
     int count = 0;
     while ((entry = readdir(dir.get()))) {
         if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
@@ -907,9 +1062,12 @@
 }
 
 TEST(logcat, logrotate_id) {
-    static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n 32 -r 1 --id=test";
-    static const char logcat_short_cmd[] = "logcat -b all -t 10 -f %s/%s -n 32 -r 1 --id=test";
-    static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+    static const char logcat_cmd[] =
+        "logcat -b all -d -f %s/%s -n 32 -r 1 --id=test";
+    static const char logcat_short_cmd[] =
+        "logcat -b all -t 10 -f %s/%s -n 32 -r 1 --id=test";
+    static const char tmp_out_dir_form[] =
+        "/data/local/tmp/logcat.logrotate.XXXXXX";
     static const char log_filename[] = "log.txt";
     char tmp_out_dir[strlen(tmp_out_dir_form) + 1];
     ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
@@ -926,19 +1084,21 @@
     unlink(id_file);
     EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
 
-    FILE *fp = fopen(id_file, "w");
+    FILE* fp = fopen(id_file, "w");
     if (fp) {
         fprintf(fp, "not_a_test");
         fclose(fp);
     }
     if (getuid() != 0) {
-        chmod(id_file, 0); // API to preserve content even with signature change
+        chmod(id_file,
+              0);  // API to preserve content even with signature change
         ASSERT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
         chmod(id_file, 0600);
     }
 
     int new_signature;
-    EXPECT_LE(2, (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir)));
+    EXPECT_LE(
+        2, (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir)));
     EXPECT_GT(34, new_signature);
 
     static const char cleanup_cmd[] = "rm -rf %s";
@@ -948,23 +1108,26 @@
 }
 
 TEST(logcat, logrotate_nodir) {
-    // expect logcat to error out on writing content and exit(1) for nodir
-    EXPECT_EQ(W_EXITCODE(1, 0),
-              system("logcat -b all -d"
-                     " -f /das/nein/gerfingerpoken/logcat/log.txt"
-                     " -n 256 -r 1024"));
+    // expect logcat to error out on writing content and not exit(0) for nodir
+    static const char command[] =
+        "logcat -b all -d"
+        " -f /das/nein/gerfingerpoken/logcat/log.txt"
+        " -n 256 -r 1024";
+    EXPECT_FALSE(IsFalse(0 == logcat_system(command), command));
 }
 
-static void caught_blocking_clear(int /*signum*/) {
+#ifndef logcat
+static void caught_blocking_clear(int signum) {
     unsigned long long v = 0xDEADBEEFA55C0000ULL;
 
     v += getpid() & 0xFFFF;
+    if (signum == 0) ++v;
 
     LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
 }
 
 TEST(logcat, blocking_clear) {
-    FILE *fp;
+    FILE* fp;
     unsigned long long v = 0xDEADBEEFA55C0000ULL;
 
     pid_t pid = getpid();
@@ -973,12 +1136,13 @@
 
     // This test is racey; an event occurs between clear and dump.
     // We accept that we will get a false positive, but never a false negative.
-    ASSERT_TRUE(NULL != (fp = popen(
-      "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
-      " logcat -b events -c 2>&1 ;"
-      " logcat -b events -g 2>&1 ;"
-      " logcat -v brief -b events 2>&1",
-      "r")));
+    ASSERT_TRUE(
+        NULL !=
+        (fp = popen("( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
+                    " logcat -b events -c 2>&1 ;"
+                    " logcat -b events -g 2>&1 ;"
+                    " logcat -v brief -b events 2>&1",
+                    "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -990,7 +1154,6 @@
     signal(SIGALRM, caught_blocking_clear);
     alarm(2);
     while (fgets(buffer, sizeof(buffer), fp)) {
-
         if (!strncmp(buffer, "clearLog: ", 10)) {
             fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
             count = signals = 1;
@@ -1004,37 +1167,38 @@
         int size, consumed, max, payload;
         char size_mult[3], consumed_mult[3];
         size = consumed = max = payload = 0;
-        if (6 == sscanf(buffer, "events: ring buffer is %d%2s (%d%2s consumed),"
-                                " max entry is %db, max payload is %db",
-                                &size, size_mult, &consumed, consumed_mult,
-                                &max, &payload)) {
+        if (6 == sscanf(buffer,
+                        "events: ring buffer is %d%2s (%d%2s consumed),"
+                        " max entry is %db, max payload is %db",
+                        &size, size_mult, &consumed, consumed_mult, &max,
+                        &payload)) {
             long full_size = size, full_consumed = consumed;
 
-            switch(size_mult[0]) {
-            case 'G':
-                full_size *= 1024;
+            switch (size_mult[0]) {
+                case 'G':
+                    full_size *= 1024;
                 /* FALLTHRU */
-            case 'M':
-                full_size *= 1024;
+                case 'M':
+                    full_size *= 1024;
                 /* FALLTHRU */
-            case 'K':
-                full_size *= 1024;
+                case 'K':
+                    full_size *= 1024;
                 /* FALLTHRU */
-            case 'b':
-                break;
+                case 'b':
+                    break;
             }
-            switch(consumed_mult[0]) {
-            case 'G':
-                full_consumed *= 1024;
+            switch (consumed_mult[0]) {
+                case 'G':
+                    full_consumed *= 1024;
                 /* FALLTHRU */
-            case 'M':
-                full_consumed *= 1024;
+                case 'M':
+                    full_consumed *= 1024;
                 /* FALLTHRU */
-            case 'K':
-                full_consumed *= 1024;
+                case 'K':
+                    full_consumed *= 1024;
                 /* FALLTHRU */
-            case 'b':
-                break;
+                case 'b':
+                    break;
             }
             EXPECT_GT(full_size, full_consumed);
             EXPECT_GT(full_size, max);
@@ -1050,8 +1214,7 @@
         int p;
         unsigned long long l;
 
-        if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l))
-         || (p != pid)) {
+        if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) || (p != pid)) {
             continue;
         }
 
@@ -1077,11 +1240,13 @@
 
     EXPECT_EQ(1, signals);
 }
+#endif
 
-static bool get_white_black(char **list) {
-    FILE *fp;
+static bool get_white_black(char** list) {
+    FILE* fp;
+    logcat_define(ctx);
 
-    fp = popen("logcat -p 2>/dev/null", "r");
+    fp = logcat_popen(ctx, "logcat -p 2>/dev/null");
     if (fp == NULL) {
         fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
         return false;
@@ -1090,12 +1255,12 @@
     char buffer[BIG_BUFFER];
 
     while (fgets(buffer, sizeof(buffer), fp)) {
-        char *hold = *list;
-        char *buf = buffer;
+        char* hold = *list;
+        char* buf = buffer;
         while (isspace(*buf)) {
             ++buf;
         }
-        char *end = buf + strlen(buf);
+        char* end = buf + strlen(buf);
         while (isspace(*--end) && (end >= buf)) {
             *end = '\0';
         }
@@ -1109,28 +1274,29 @@
             asprintf(list, "%s", buf);
         }
     }
-    pclose(fp);
+    logcat_pclose(ctx, fp);
     return *list != NULL;
 }
 
-static bool set_white_black(const char *list) {
-    FILE *fp;
+static bool set_white_black(const char* list) {
+    FILE* fp;
+    logcat_define(ctx);
 
     char buffer[BIG_BUFFER];
 
     snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list ? list : "");
-    fp = popen(buffer, "r");
+    fp = logcat_popen(ctx, buffer);
     if (fp == NULL) {
         fprintf(stderr, "ERROR: %s\n", buffer);
         return false;
     }
 
     while (fgets(buffer, sizeof(buffer), fp)) {
-        char *buf = buffer;
+        char* buf = buffer;
         while (isspace(*buf)) {
             ++buf;
         }
-        char *end = buf + strlen(buf);
+        char* end = buf + strlen(buf);
         while ((end > buf) && isspace(*--end)) {
             *end = '\0';
         }
@@ -1138,15 +1304,15 @@
             continue;
         }
         fprintf(stderr, "%s\n", buf);
-        pclose(fp);
+        logcat_pclose(ctx, fp);
         return false;
     }
-    return pclose(fp) == 0;
+    return logcat_pclose(ctx, fp) == 0;
 }
 
 TEST(logcat, white_black_adjust) {
-    char *list = NULL;
-    char *adjust = NULL;
+    char* list = NULL;
+    char* adjust = NULL;
 
     get_white_black(&list);
 
@@ -1175,55 +1341,72 @@
 }
 
 TEST(logcat, regex) {
-    FILE *fp;
+    FILE* fp;
+    logcat_define(ctx);
     int count = 0;
 
     char buffer[BIG_BUFFER];
+// Have to make liblogcat data unique from logcat data injection
+#ifdef logcat
+#define logcat_regex_prefix "lolcat_test"
+#else
+#define logcat_regex_prefix "logcat_test"
+#endif
 
-    snprintf(buffer, sizeof(buffer), "logcat --pid %d -d -e logcat_test_a+b", getpid());
+    snprintf(buffer, sizeof(buffer),
+             "logcat --pid %d -d -e " logcat_regex_prefix "_a+b", getpid());
 
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_ab"));
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_b"));
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_aaaab"));
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_aaaa"));
-
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+                                          logcat_regex_prefix "_ab"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+                                          logcat_regex_prefix "_b"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+                                          logcat_regex_prefix "_aaaab"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+                                          logcat_regex_prefix "_aaaa"));
     // Let the logs settle
     sleep(1);
 
-    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
             continue;
         }
 
-        EXPECT_TRUE(strstr(buffer, "logcat_test_") != NULL);
+        EXPECT_TRUE(strstr(buffer, logcat_regex_prefix "_") != NULL);
 
         count++;
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     ASSERT_EQ(2, count);
 }
 
 TEST(logcat, maxcount) {
-    FILE *fp;
+    FILE* fp;
+    logcat_define(ctx);
     int count = 0;
 
     char buffer[BIG_BUFFER];
 
-    snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3", getpid());
+    snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3",
+             getpid());
 
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+    LOG_FAILURE_RETRY(
+        __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+    LOG_FAILURE_RETRY(
+        __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+    LOG_FAILURE_RETRY(
+        __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+    LOG_FAILURE_RETRY(
+        __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
 
     // Let the logs settle
     sleep(1);
 
-    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
@@ -1233,7 +1416,7 @@
         count++;
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     ASSERT_EQ(3, count);
 }
@@ -1245,8 +1428,18 @@
     ;
 
 static bool End_to_End(const char* tag, const char* fmt, ...) {
-    FILE *fp = popen("logcat -v brief -b events -v descriptive -t 100 2>/dev/null", "r");
-    if (!fp) return false;
+    logcat_define(ctx);
+    FILE* fp = logcat_popen(ctx,
+                            "logcat"
+                            " -v brief"
+                            " -b events"
+                            " -v descriptive"
+                            " -t 100"
+                            " 2>/dev/null");
+    if (!fp) {
+        fprintf(stderr, "End_to_End: popen failed");
+        return false;
+    }
 
     char buffer[BIG_BUFFER];
     va_list ap;
@@ -1255,7 +1448,7 @@
     vsnprintf(buffer, sizeof(buffer), fmt, ap);
     va_end(ap);
 
-    char *str = NULL;
+    char* str = NULL;
     asprintf(&str, "I/%s ( %%d):%%c%s%%c", tag, buffer);
     std::string expect(str);
     free(str);
@@ -1277,15 +1470,19 @@
         }
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     if ((count == 0) && (lastMatch.length() > 0)) {
         // 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) {
+        fprintf(stderr, "Too many matches (%d) for %s\n", count, expect.c_str());
     }
 
-    return count == 1;
+    // 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;
 }
 
 TEST(logcat, descriptive) {
@@ -1300,8 +1497,8 @@
         static const char theAnswer[] = "what is five by seven";
         ctx << theAnswer;
         ctx.write();
-        EXPECT_TRUE(End_to_End(hhgtg.tagStr,
-                               "to life the universe etc=%s", theAnswer));
+        EXPECT_TRUE(
+            End_to_End(hhgtg.tagStr, "to life the universe etc=%s", theAnswer));
     }
 
     {
@@ -1312,8 +1509,7 @@
             ctx << id << (int32_t)42 << (int32_t)-1 << (int32_t)0;
             ctx.write();
             EXPECT_TRUE(End_to_End(sync.tagStr,
-                                   "[id=%s,event=42,source=-1,account=0]",
-                                   id));
+                                   "[id=%s,event=42,source=-1,account=0]", id));
         }
 
         // Partial match to description
@@ -1321,9 +1517,7 @@
             android_log_event_list ctx(sync.tagNo);
             ctx << id << (int32_t)43 << (int64_t)-1 << (int32_t)0;
             ctx.write();
-            EXPECT_TRUE(End_to_End(sync.tagStr,
-                                   "[id=%s,event=43,-1,0]",
-                                   id));
+            EXPECT_TRUE(End_to_End(sync.tagStr, "[id=%s,event=43,-1,0]", id));
         }
 
         // Negative Test of End_to_End, ensure it is working
@@ -1332,9 +1526,8 @@
             ctx << id << (int32_t)44 << (int32_t)-1 << (int64_t)0;
             ctx.write();
             fprintf(stderr, "Expect a \"Closest match\" message\n");
-            EXPECT_FALSE(End_to_End(sync.tagStr,
-                                    "[id=%s,event=44,source=-1,account=0]",
-                                    id));
+            EXPECT_FALSE(End_to_End(
+                sync.tagStr, "[id=%s,event=44,source=-1,account=0]", id));
         }
     }
 
@@ -1344,16 +1537,16 @@
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint64_t)30 << (int32_t)2;
             ctx.write();
-            EXPECT_TRUE(End_to_End(sync.tagStr,
-                                   "[aggregation time=30ms,count=2]"));
+            EXPECT_TRUE(
+                End_to_End(sync.tagStr, "[aggregation time=30ms,count=2]"));
         }
 
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint64_t)31570 << (int32_t)911;
             ctx.write();
-            EXPECT_TRUE(End_to_End(sync.tagStr,
-                                   "[aggregation time=31.57s,count=911]"));
+            EXPECT_TRUE(
+                End_to_End(sync.tagStr, "[aggregation time=31.57s,count=911]"));
         }
     }
 
@@ -1396,7 +1589,7 @@
 
         {
             android_log_event_list ctx(sync.tagNo);
-            ctx << (uint32_t)3221225472; // 3MB, but on purpose overflowed
+            ctx << (uint32_t)3221225472;  // 3MB, but on purpose overflowed
             ctx.write();
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=-1GB"));
         }
@@ -1409,3 +1602,23 @@
         EXPECT_TRUE(End_to_End(sync.tagStr, ""));
     }
 }
+
+static bool reportedSecurity(const char* command) {
+    logcat_define(ctx);
+    FILE* fp = logcat_popen(ctx, command);
+    if (!fp) return true;
+
+    std::string ret;
+    bool val = android::base::ReadFdToString(fileno(fp), &ret);
+    logcat_pclose(ctx, fp);
+
+    if (!val) return true;
+    return std::string::npos != ret.find("'security'");
+}
+
+TEST(logcat, security) {
+    EXPECT_FALSE(reportedSecurity("logcat -b all -g 2>&1"));
+    EXPECT_TRUE(reportedSecurity("logcat -b security -g 2>&1"));
+    EXPECT_TRUE(reportedSecurity("logcat -b security -c 2>&1"));
+    EXPECT_TRUE(reportedSecurity("logcat -b security -G 256K 2>&1"));
+}
diff --git a/logd/Android.mk b/logd/Android.mk
index 7fe48d7..9211037 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -21,6 +21,7 @@
     libaudit.c \
     LogAudit.cpp \
     LogKlog.cpp \
+    LogTags.cpp \
     event.logtags
 
 LOCAL_SHARED_LIBRARIES := \
@@ -38,11 +39,23 @@
 #        $(LOCAL_PATH)/$2/event.logtags)
 #  event_flag := $(call event_logtags,auditd)
 #  event_flag += $(call event_logtags,logd)
+#  event_flag += $(call event_logtags,tag_def)
 # so make sure we do not regret hard-coding it as follows:
-event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004
+event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004 -DTAG_DEF_LOG_TAG=1005
+event_flag += -DLIBLOG_LOG_TAG=1006
 
 LOCAL_CFLAGS := -Werror $(event_flag)
 
 include $(BUILD_EXECUTABLE)
 
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := logtagd.rc
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
+
+include $(BUILD_PREBUILT)
+
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 52c6742..6ad7351 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <arpa/inet.h>
+#include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -47,6 +48,7 @@
     registerCmd(new GetStatisticsCmd(buf));
     registerCmd(new SetPruneListCmd(buf));
     registerCmd(new GetPruneListCmd(buf));
+    registerCmd(new GetEventTagCmd(buf));
     registerCmd(new ReinitCmd());
     registerCmd(new ExitCmd(this));
 }
@@ -284,6 +286,57 @@
     return 0;
 }
 
+CommandListener::GetEventTagCmd::GetEventTagCmd(LogBuffer *buf) :
+        LogCommand("getEventTag"),
+        mBuf(*buf) {
+}
+
+int CommandListener::GetEventTagCmd::runCommand(SocketClient *cli,
+                                         int argc, char ** argv) {
+    setname();
+    uid_t uid = cli->getUid();
+    if (clientHasLogCredentials(cli)) {
+        uid = AID_ROOT;
+    }
+
+    const char *name = NULL;
+    const char *format = NULL;
+    const char *id = NULL;
+    for (int i = 1; i < argc; ++i) {
+        static const char _name[] = "name=";
+        if (!strncmp(argv[i], _name, strlen(_name))) {
+            name = argv[i] + strlen(_name);
+            continue;
+        }
+
+        static const char _format[] = "format=";
+        if (!strncmp(argv[i], _format, strlen(_format))) {
+            format = argv[i] + strlen(_format);
+            continue;
+        }
+
+        static const char _id[] = "id=";
+        if (!strncmp(argv[i], _id, strlen(_id))) {
+            id = argv[i] + strlen(_id);
+            continue;
+        }
+    }
+
+    if (id) {
+        if (format || name) {
+            cli->sendMsg("can not mix id= with either format= or name=");
+            return 0;
+        }
+        cli->sendMsg(package_string(mBuf.formatEntry(atoi(id), uid)).c_str());
+        return 0;
+    }
+
+    cli->sendMsg(package_string(mBuf.formatGetEventTag(uid,
+                                                       name, format)).c_str());
+
+    return 0;
+}
+
 CommandListener::ReinitCmd::ReinitCmd() : LogCommand("reinit") {
 }
 
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index 5d50177..39de03b 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -61,6 +61,7 @@
     LogBufferCmd(GetStatistics);
     LogBufferCmd(GetPruneList);
     LogBufferCmd(SetPruneList);
+    LogBufferCmd(GetEventTag);
 
 #define LogCmd(name)                                             \
     class name##Cmd : public LogCommand {                        \
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index c3ccd84..92d957f 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -34,6 +34,7 @@
 #include "LogBuffer.h"
 #include "LogKlog.h"
 #include "LogReader.h"
+#include "LogUtils.h"
 
 #define KMSG_PRIORITY(PRI)                          \
     '<',                                            \
@@ -42,23 +43,70 @@
     '>'
 
 LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg) :
-        SocketListener(getLogSocket(), false),
+        SocketListener(mSock = getLogSocket(), false),
         logbuf(buf),
         reader(reader),
         fdDmesg(fdDmesg),
-        initialized(false) {
+        main(__android_logger_property_get_bool("ro.logd.auditd.main",
+                                                BOOL_DEFAULT_TRUE)),
+        events(__android_logger_property_get_bool("ro.logd.auditd.events",
+                                                  BOOL_DEFAULT_TRUE)),
+        initialized(false),
+        tooFast(false) {
     static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
         'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':',
         ' ', 's', 't', 'a', 'r', 't', '\n' };
     write(fdDmesg, auditd_message, sizeof(auditd_message));
 }
 
+void LogAudit::checkRateLimit() {
+
+    // trim list for AUDIT_RATE_LIMIT_BURST_DURATION of history
+    log_time oldest(AUDIT_RATE_LIMIT_BURST_DURATION, 0);
+    bucket.emplace(android_log_clockid());
+    oldest = bucket.back() - oldest;
+    while (bucket.front() < oldest) bucket.pop();
+
+    static const size_t upperThreshold =
+        ((AUDIT_RATE_LIMIT_BURST_DURATION *
+          (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) + 1) /
+                              2;
+    if (bucket.size() >= upperThreshold) {
+        // Hit peak, slow down source
+        if (!tooFast) {
+            tooFast = true;
+            audit_rate_limit(mSock, AUDIT_RATE_LIMIT_MAX);
+        }
+
+        // We do not need to hold on to the full set of timing data history,
+        // let's ensure it does not grow without bounds.  This also ensures
+        // that std::dequeue underneath behaves almost like a ring buffer.
+        do {
+            bucket.pop();
+        } while (bucket.size() >= upperThreshold);
+        return;
+    }
+
+    if (!tooFast) return;
+
+    static const size_t lowerThreshold = AUDIT_RATE_LIMIT_BURST_DURATION *
+                                         AUDIT_RATE_LIMIT_MAX;
+
+    if (bucket.size() >= lowerThreshold) return;
+
+    tooFast = false;
+    // Went below max sustained rate, allow source to speed up
+    audit_rate_limit(mSock, AUDIT_RATE_LIMIT_DEFAULT);
+}
+
 bool LogAudit::onDataAvailable(SocketClient *cli) {
     if (!initialized) {
         prctl(PR_SET_NAME, "logd.auditd");
         initialized = true;
     }
 
+    checkRateLimit();
+
     struct audit_message rep;
 
     rep.nlh.nlmsg_type = 0;
@@ -70,8 +118,7 @@
         return false;
     }
 
-    logPrint("type=%d %.*s",
-        rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data);
+    logPrint("type=%d %.*s", rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data);
 
     return true;
 }
@@ -93,6 +140,13 @@
     }
 
     char *cp;
+    // Work around kernels missing
+    // https://github.com/torvalds/linux/commit/b8f89caafeb55fba75b74bea25adc4e4cd91be67
+    // Such kernels improperly add newlines inside audit messages.
+    while ((cp = strchr(str, '\n'))) {
+        *cp = ' ';
+    }
+
     while ((cp = strstr(str, "  "))) {
         memmove(cp, cp + 1, strlen(cp + 1) + 1);
     }
@@ -117,7 +171,8 @@
             if (avcl) {
                 char *avcr = strstr(str, avc);
 
-                skip = avcr && !strcmp(avcl + strlen(avc), avcr + strlen(avc));
+                skip = avcr && !fastcmp<strcmp>(avcl + strlen(avc),
+                                                avcr + strlen(avc));
                 if (skip) {
                     ++count;
                     free(last_str);
@@ -170,6 +225,11 @@
         }
     }
 
+    if (!main && !events) {
+        free(str);
+        return 0;
+    }
+
     pid_t pid = getpid();
     pid_t tid = gettid();
     uid_t uid = AID_LOGD;
@@ -220,7 +280,7 @@
 
     bool notify = false;
 
-    {   // begin scope for event buffer
+    if (events) {   // begin scope for event buffer
         uint32_t buffer[(n + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
 
         android_log_event_string_t *event
@@ -275,7 +335,7 @@
     size_t e = strnlen(ecomm, LOGGER_ENTRY_MAX_PAYLOAD - b);
     n = b + e + l + 2;
 
-    {   // begin scope for main buffer
+    if (main) {   // begin scope for main buffer
         char newstr[n];
 
         *newstr = info ? ANDROID_LOG_INFO : ANDROID_LOG_WARN;
@@ -333,5 +393,6 @@
         audit_close(fd);
         fd = -1;
     }
+    (void)audit_rate_limit(fd, AUDIT_RATE_LIMIT_DEFAULT);
     return fd;
 }
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index ab30e28..045d631 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -17,6 +17,8 @@
 #ifndef _LOGD_LOG_AUDIT_H__
 #define _LOGD_LOG_AUDIT_H__
 
+#include <queue>
+
 #include <sysutils/SocketListener.h>
 
 #include "LogBuffer.h"
@@ -26,9 +28,16 @@
 class LogAudit : public SocketListener {
     LogBuffer *logbuf;
     LogReader *reader;
-    int fdDmesg;
+    int fdDmesg; // fdDmesg >= 0 is functionally bool dmesg
+    bool main;
+    bool events;
     bool initialized;
 
+    bool tooFast;
+    int mSock;
+    std::queue<log_time> bucket;
+    void checkRateLimit();
+
 public:
     LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg);
     int log(char *buf, size_t len);
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index a009433..e03731b 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -17,6 +17,7 @@
 //#define DEBUG_CHECK_FOR_STALE_ENTRIES
 
 #include <ctype.h>
+#include <endian.h>
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
@@ -33,6 +34,7 @@
 #include "LogBuffer.h"
 #include "LogKlog.h"
 #include "LogReader.h"
+#include "LogUtils.h"
 
 #ifndef __predict_false
 #define __predict_false(exp) __builtin_expect((exp) != 0, 0)
@@ -110,9 +112,82 @@
         mTimes(*times) {
     pthread_mutex_init(&mLogElementsLock, NULL);
 
+    log_id_for_each(i) {
+        lastLoggedElements[i] = NULL;
+        droppedElements[i] = NULL;
+    }
+
     init();
 }
 
+LogBuffer::~LogBuffer() {
+    log_id_for_each(i) {
+        delete lastLoggedElements[i];
+        delete droppedElements[i];
+    }
+}
+
+enum match_type {
+    DIFFERENT,
+    SAME,
+    SAME_LIBLOG
+};
+
+static enum match_type identical(LogBufferElement* elem, LogBufferElement* last) {
+    // is it mostly identical?
+//  if (!elem) return DIFFERENT;
+    unsigned short lenl = elem->getMsgLen();
+    if (!lenl) return DIFFERENT;
+//  if (!last) return DIFFERENT;
+    unsigned short lenr = last->getMsgLen();
+    if (!lenr) return DIFFERENT;
+//  if (elem->getLogId() != last->getLogId()) return DIFFERENT;
+    if (elem->getUid() != last->getUid()) return DIFFERENT;
+    if (elem->getPid() != last->getPid()) return DIFFERENT;
+    if (elem->getTid() != last->getTid()) return DIFFERENT;
+
+    // last is more than a minute old, stop squashing identical messages
+    if (elem->getRealTime().nsec() >
+        (last->getRealTime().nsec() + 60 * NS_PER_SEC)) return DIFFERENT;
+
+    // Identical message
+    const char* msgl = elem->getMsg();
+    const char* msgr = last->getMsg();
+    if (lenl == lenr) {
+        if (!fastcmp<memcmp>(msgl, msgr, lenl)) return SAME;
+        // liblog tagged messages (content gets summed)
+        if ((elem->getLogId() == LOG_ID_EVENTS) &&
+            (lenl == sizeof(android_log_event_int_t)) &&
+            !fastcmp<memcmp>(msgl, msgr,
+                             sizeof(android_log_event_int_t) - sizeof(int32_t)) &&
+            (elem->getTag() == LIBLOG_LOG_TAG)) return SAME_LIBLOG;
+    }
+
+    // audit message (except sequence number) identical?
+    static const char avc[] = "): avc: ";
+
+    if (last->isBinary()) {
+        if (fastcmp<memcmp>(msgl, msgr,
+                            sizeof(android_log_event_string_t) -
+                                sizeof(int32_t))) return DIFFERENT;
+        msgl += sizeof(android_log_event_string_t);
+        lenl -= sizeof(android_log_event_string_t);
+        msgr += sizeof(android_log_event_string_t);
+        lenr -= sizeof(android_log_event_string_t);
+    }
+    const char *avcl = android::strnstr(msgl, lenl, avc);
+    if (!avcl) return DIFFERENT;
+    lenl -= avcl - msgl;
+    const char *avcr = android::strnstr(msgr, lenr, avc);
+    if (!avcr) return DIFFERENT;
+    lenr -= avcr - msgr;
+    if (lenl != lenr) return DIFFERENT;
+    if (fastcmp<memcmp>(avcl + strlen(avc),
+                        avcr + strlen(avc),
+                        lenl - strlen(avc))) return DIFFERENT;
+    return SAME;
+}
+
 int LogBuffer::log(log_id_t log_id, log_time realtime,
                    uid_t uid, pid_t pid, pid_t tid,
                    const char *msg, unsigned short len) {
@@ -125,15 +200,13 @@
     if (log_id != LOG_ID_SECURITY) {
         int prio = ANDROID_LOG_INFO;
         const char *tag = NULL;
-        size_t len = 0;
         if (log_id == LOG_ID_EVENTS) {
-            tag = android::tagToName(&len, elem->getTag());
+            tag = tagToName(elem->getTag());
         } else {
             prio = *msg;
             tag = msg + 1;
-            len = strlen(tag);
         }
-        if (!__android_log_is_loggable_len(prio, tag, len, ANDROID_LOG_VERBOSE)) {
+        if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
             // Log traffic received to total
             pthread_mutex_lock(&mLogElementsLock);
             stats.add(elem);
@@ -145,14 +218,164 @@
     }
 
     pthread_mutex_lock(&mLogElementsLock);
+    LogBufferElement* currentLast = lastLoggedElements[log_id];
+    if (currentLast) {
+        LogBufferElement *dropped = droppedElements[log_id];
+        unsigned short count = dropped ? dropped->getDropped() : 0;
+        //
+        // State Init
+        //     incoming:
+        //         dropped = NULL
+        //         currentLast = NULL;
+        //         elem = incoming message
+        //     outgoing:
+        //         dropped = NULL -> State 0
+        //         currentLast = copy of elem
+        //         log elem
+        // State 0
+        //     incoming:
+        //         count = 0
+        //         dropped = NULL
+        //         currentLast = copy of last message
+        //         elem = incoming message
+        //     outgoing: if match != DIFFERENT
+        //         dropped = copy of first identical message -> State 1
+        //         currentLast = reference to elem
+        //     break: if match == DIFFERENT
+        //         dropped = NULL -> State 0
+        //         delete copy of last message (incoming currentLast)
+        //         currentLast = copy of elem
+        //         log elem
+        // State 1
+        //     incoming:
+        //         count = 0
+        //         dropped = copy of first identical message
+        //         currentLast = reference to last held-back incoming
+        //                       message
+        //         elem = incoming message
+        //     outgoing: if match == SAME
+        //         delete copy of first identical message (dropped)
+        //         dropped = reference to last held-back incoming
+        //                   message set to chatty count of 1 -> State 2
+        //         currentLast = reference to elem
+        //     outgoing: if match == SAME_LIBLOG
+        //         dropped = copy of first identical message -> State 1
+        //         take sum of currentLast and elem
+        //         if sum overflows:
+        //             log currentLast
+        //             currentLast = reference to elem
+        //         else
+        //             delete currentLast
+        //             currentLast = reference to elem, sum liblog.
+        //     break: if match == DIFFERENT
+        //         delete dropped
+        //         dropped = NULL -> State 0
+        //         log reference to last held-back (currentLast)
+        //         currentLast = copy of elem
+        //         log elem
+        // State 2
+        //     incoming:
+        //         count = chatty count
+        //         dropped = chatty message holding count
+        //         currentLast = reference to last held-back incoming
+        //                       message.
+        //         dropped = chatty message holding count
+        //         elem = incoming message
+        //     outgoing: if match != DIFFERENT
+        //         delete chatty message holding count
+        //         dropped = reference to last held-back incoming
+        //                   message, set to chatty count + 1
+        //         currentLast = reference to elem
+        //     break: if match == DIFFERENT
+        //         log dropped (chatty message)
+        //         dropped = NULL -> State 0
+        //         log reference to last held-back (currentLast)
+        //         currentLast = copy of elem
+        //         log elem
+        //
+        enum match_type match = identical(elem, currentLast);
+        if (match != DIFFERENT) {
+            if (dropped) {
+                // Sum up liblog tag messages?
+                if ((count == 0) /* at Pass 1 */ && (match == SAME_LIBLOG)) {
+                    android_log_event_int_t* event =
+                        reinterpret_cast<android_log_event_int_t*>(
+                            const_cast<char*>(currentLast->getMsg()));
+                    //
+                    // To unit test, differentiate with something like:
+                    //    event->header.tag = htole32(CHATTY_LOG_TAG);
+                    // here, then instead of delete currentLast below,
+                    // log(currentLast) to see the incremental sums form.
+                    //
+                    uint32_t swab = event->payload.data;
+                    unsigned long long total = htole32(swab);
+                    event = reinterpret_cast<android_log_event_int_t*>(
+                            const_cast<char*>(elem->getMsg()));
+                    swab = event->payload.data;
 
+                    lastLoggedElements[LOG_ID_EVENTS] = elem;
+                    total += htole32(swab);
+                    // check for overflow
+                    if (total >= UINT32_MAX) {
+                        log(currentLast);
+                        pthread_mutex_unlock(&mLogElementsLock);
+                        return len;
+                    }
+                    stats.add(currentLast);
+                    stats.subtract(currentLast);
+                    delete currentLast;
+                    swab = total;
+                    event->payload.data = htole32(swab);
+                    pthread_mutex_unlock(&mLogElementsLock);
+                    return len;
+                }
+                if (count == USHRT_MAX) {
+                    log(dropped);
+                    count = 1;
+                } else {
+                    delete dropped;
+                    ++count;
+                }
+            }
+            if (count) {
+                stats.add(currentLast);
+                stats.subtract(currentLast);
+                currentLast->setDropped(count);
+            }
+            droppedElements[log_id] = currentLast;
+            lastLoggedElements[log_id] = elem;
+            pthread_mutex_unlock(&mLogElementsLock);
+            return len;
+        }
+        if (dropped) { // State 1 or 2
+            if (count) { // State 2
+               log(dropped); // report chatty
+            } else { // State 1
+               delete dropped;
+            }
+            droppedElements[log_id] = NULL;
+            log(currentLast); // report last message in the series
+        } else { // State 0
+            delete currentLast;
+        }
+    }
+    lastLoggedElements[log_id] = new LogBufferElement(*elem);
+
+    log(elem);
+    pthread_mutex_unlock(&mLogElementsLock);
+
+    return len;
+}
+
+// assumes mLogElementsLock held, owns elem, will look after garbage collection
+void LogBuffer::log(LogBufferElement* elem) {
     // Insert elements in time sorted order if possible
     //  NB: if end is region locked, place element at end of list
     LogBufferElementCollection::iterator it = mLogElements.end();
     LogBufferElementCollection::iterator last = it;
     while (last != mLogElements.begin()) {
         --it;
-        if ((*it)->getRealTime() <= realtime) {
+        if ((*it)->getRealTime() <= elem->getRealTime()) {
             break;
         }
         last = it;
@@ -169,7 +392,7 @@
 
         LastLogTimes::iterator times = mTimes.begin();
         while(times != mTimes.end()) {
-            LogTimeEntry *entry = (*times);
+            LogTimeEntry* entry = (*times);
             if (entry->owned_Locked()) {
                 if (!entry->mNonBlock) {
                     end_always = true;
@@ -187,17 +410,14 @@
                 || (end_set && (end >= (*last)->getSequence()))) {
             mLogElements.push_back(elem);
         } else {
-            mLogElements.insert(last,elem);
+            mLogElements.insert(last, elem);
         }
 
         LogTimeEntry::unlock();
     }
 
     stats.add(elem);
-    maybePrune(log_id);
-    pthread_mutex_unlock(&mLogElementsLock);
-
-    return len;
+    maybePrune(elem->getLogId());
 }
 
 // Prune at most 10% of the log entries or maxPrune, whichever is less.
@@ -313,10 +533,9 @@
 class LogBufferElementKey {
     const union {
         struct {
-            uint16_t uid;
+            uint32_t uid;
             uint16_t pid;
             uint16_t tid;
-            uint16_t padding;
         } __packed;
         uint64_t value;
     } __packed;
@@ -325,8 +544,8 @@
     LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid):
             uid(uid),
             pid(pid),
-            tid(tid),
-            padding(0) {
+            tid(tid)
+    {
     }
     explicit LogBufferElementKey(uint64_t key):value(key) { }
 
@@ -880,6 +1099,10 @@
         }
     }
 
+    // Help detect if the valid message before is from the same source so
+    // we can differentiate chatty filter types.
+    pid_t lastTid[LOG_ID_MAX] = { 0 };
+
     for (; it != mLogElements.end(); ++it) {
         LogBufferElement *element = *it;
 
@@ -906,10 +1129,19 @@
             }
         }
 
+        bool sameTid = lastTid[element->getLogId()] == element->getTid();
+        // Dropped (chatty) immediately following a valid log from the
+        // same source in the same log buffer indicates we have a
+        // multiple identical squash.  chatty that differs source
+        // is due to spam filter.  chatty to chatty of different
+        // source is also due to spam filter.
+        lastTid[element->getLogId()] = (element->getDropped() && !sameTid) ?
+                0 : element->getTid();
+
         pthread_mutex_unlock(&mLogElementsLock);
 
         // range locking in LastLogTimes looks after us
-        max = element->flushTo(reader, this, privileged);
+        max = element->flushTo(reader, this, privileged, sameTid);
 
         if (max == element->FLUSH_ERROR) {
             return max;
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index ff9692e..9feef32 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -27,6 +27,7 @@
 #include <sysutils/SocketClient.h>
 
 #include "LogBufferElement.h"
+#include "LogTags.h"
 #include "LogTimes.h"
 #include "LogStatistics.h"
 #include "LogWhiteBlackList.h"
@@ -99,10 +100,17 @@
 
     bool monotonic;
 
+    LogTags tags;
+
+    LogBufferElement* lastLoggedElements[LOG_ID_MAX];
+    LogBufferElement* droppedElements[LOG_ID_MAX];
+    void log(LogBufferElement* elem);
+
 public:
     LastLogTimes &mTimes;
 
     explicit LogBuffer(LastLogTimes *times);
+    ~LogBuffer();
     void init();
     bool isMonotonic() { return monotonic; }
 
@@ -128,6 +136,15 @@
     int initPrune(const char *cp) { return mPrune.init(cp); }
     std::string formatPrune() { return mPrune.format(); }
 
+    std::string formatGetEventTag(uid_t uid,
+                                  const char *name, const char *format) {
+        return tags.formatGetEventTag(uid, name, format);
+    }
+    std::string formatEntry(uint32_t tag, uid_t uid) {
+        return tags.formatEntry(tag, uid);
+    }
+    const char *tagToName(uint32_t tag) { return tags.tagToName(tag); }
+
     // helper must be protected directly or implicitly by lock()/unlock()
     const char *pidToName(pid_t pid) { return stats.pidToName(pid); }
     uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index f5c60c7..a651fd4 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -50,6 +50,19 @@
         0;
 }
 
+LogBufferElement::LogBufferElement(const LogBufferElement &elem) :
+        mTag(elem.mTag),
+        mUid(elem.mUid),
+        mPid(elem.mPid),
+        mTid(elem.mTid),
+        mSequence(elem.mSequence),
+        mRealTime(elem.mRealTime),
+        mMsgLen(elem.mMsgLen),
+        mLogId(elem.mLogId) {
+    mMsg = new char[mMsgLen];
+    memcpy(mMsg, elem.mMsg, mMsgLen);
+}
+
 LogBufferElement::~LogBufferElement() {
     delete [] mMsg;
 }
@@ -89,7 +102,7 @@
         size_t name_len = strlen(name);
         // KISS: ToDo: Only checks prefix truncated, not suffix, or both
         if ((retval_len < name_len)
-                && !fast<strcmp>(retval, name + name_len - retval_len)) {
+                && !fastcmp<strcmp>(retval, name + name_len - retval_len)) {
             free(retval);
             retval = name;
         } else {
@@ -100,8 +113,8 @@
 }
 
 // assumption: mMsg == NULL
-size_t LogBufferElement::populateDroppedMessage(char *&buffer,
-        LogBuffer *parent) {
+size_t LogBufferElement::populateDroppedMessage(char*& buffer,
+        LogBuffer* parent, bool lastSame) {
     static const char tag[] = "chatty";
 
     if (!__android_log_is_loggable_len(ANDROID_LOG_INFO,
@@ -110,7 +123,7 @@
         return 0;
     }
 
-    static const char format_uid[] = "uid=%u%s%s expire %u line%s";
+    static const char format_uid[] = "uid=%u%s%s %s %u line%s";
     parent->lock();
     const char *name = parent->uidToName(mUid);
     parent->unlock();
@@ -152,13 +165,12 @@
         }
     }
     // identical to below to calculate the buffer size required
+    const char* type = lastSame ? "identical" : "expire";
     size_t len = snprintf(NULL, 0, format_uid, mUid, name ? name : "",
-                          commName ? commName : "",
+                          commName ? commName : "", type,
                           mDropped, (mDropped > 1) ? "s" : "");
 
     size_t hdrLen;
-    // LOG_ID_SECURITY not strictly needed since spam filter not activated,
-    // but required for accuracy.
     if (isBinary()) {
         hdrLen = sizeof(android_log_event_string_t);
     } else {
@@ -187,7 +199,7 @@
     }
 
     snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
-             commName ? commName : "",
+             commName ? commName : "", type,
              mDropped, (mDropped > 1) ? "s" : "");
     free(const_cast<char *>(name));
     free(const_cast<char *>(commName));
@@ -195,8 +207,8 @@
     return retval;
 }
 
-uint64_t LogBufferElement::flushTo(SocketClient *reader, LogBuffer *parent,
-                                   bool privileged) {
+uint64_t LogBufferElement::flushTo(SocketClient* reader, LogBuffer* parent,
+                                   bool privileged, bool lastSame) {
     struct logger_entry_v4 entry;
 
     memset(&entry, 0, sizeof(struct logger_entry_v4));
@@ -218,7 +230,7 @@
     char *buffer = NULL;
 
     if (!mMsg) {
-        entry.len = populateDroppedMessage(buffer, parent);
+        entry.len = populateDroppedMessage(buffer, parent, lastSame);
         if (!entry.len) {
             return mSequence;
         }
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index fb7fbed..bd98b9c 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -53,12 +53,14 @@
     static atomic_int_fast64_t sequence;
 
     // assumption: mMsg == NULL
-    size_t populateDroppedMessage(char *&buffer,
-                                  LogBuffer *parent);
+    size_t populateDroppedMessage(char*& buffer,
+                                  LogBuffer* parent,
+                                  bool lastSame);
 public:
     LogBufferElement(log_id_t log_id, log_time realtime,
                      uid_t uid, pid_t pid, pid_t tid,
                      const char *msg, unsigned short len);
+    LogBufferElement(const LogBufferElement &elem);
     virtual ~LogBufferElement();
 
     bool isBinary(void) const {
@@ -79,12 +81,14 @@
         return mDropped = value;
     }
     unsigned short getMsgLen() const { return mMsg ? mMsgLen : 0; }
+    const char* getMsg() const { return mMsg; }
     uint64_t getSequence(void) const { return mSequence; }
     static uint64_t getCurrentSequence(void) { return sequence.load(memory_order_relaxed); }
     log_time getRealTime(void) const { return mRealTime; }
 
     static const uint64_t FLUSH_ERROR;
-    uint64_t flushTo(SocketClient *writer, LogBuffer *parent, bool privileged);
+    uint64_t flushTo(SocketClient* writer, LogBuffer* parent,
+                     bool privileged, bool lastSame);
 };
 
 #endif
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 7073535..f224079 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -306,22 +306,18 @@
 static const char resumeStr[] = "PM: suspend exit ";
 static const char suspendedStr[] = "Suspended for ";
 
-static const char *strnstr(const char *s, size_t len, const char *needle) {
+const char* android::strnstr(const char* s, size_t len, const char* needle) {
     char c;
 
-    if (!len) {
-        return NULL;
-    }
+    if (!len) return NULL;
     if ((c = *needle++) != 0) {
         size_t needleLen = strlen(needle);
         do {
             do {
-                if (len <= needleLen) {
-                    return NULL;
-                }
+                if (len <= needleLen) return NULL;
                 --len;
             } while (*s++ != c);
-        } while (fast<memcmp>(s, needle, needleLen));
+        } while (fastcmp<memcmp>(s, needle, needleLen));
         s--;
     }
     return s;
@@ -349,25 +345,25 @@
             return;
         }
 
-        const char *b;
-        if (((b = strnstr(cp, len, suspendStr)))
+        const char* b;
+        if (((b = android::strnstr(cp, len, suspendStr)))
                 && ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
             len -= b - cp;
             calculateCorrection(now, b, len);
-        } else if (((b = strnstr(cp, len, resumeStr)))
+        } else if (((b = android::strnstr(cp, len, resumeStr)))
                 && ((size_t)((b += sizeof(resumeStr) - 1) - cp) < len)) {
             len -= b - cp;
             calculateCorrection(now, b, len);
-        } else if (((b = strnstr(cp, len, healthd)))
+        } else if (((b = android::strnstr(cp, len, healthd)))
                 && ((size_t)((b += sizeof(healthd) - 1) - cp) < len)
-                && ((b = strnstr(b, len -= b - cp, battery)))
+                && ((b = android::strnstr(b, len -= b - cp, battery)))
                 && ((size_t)((b += sizeof(battery) - 1) - cp) < len)) {
             // NB: healthd is roughly 150us late, so we use it instead to
             //     trigger a check for ntp-induced or hardware clock drift.
             log_time real(CLOCK_REALTIME);
             log_time mono(CLOCK_MONOTONIC);
             correction = (real < mono) ? log_time::EPOCH : (real - mono);
-        } else if (((b = strnstr(cp, len, suspendedStr)))
+        } else if (((b = android::strnstr(cp, len, suspendedStr)))
                 && ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
             len -= b - cp;
             log_time real;
@@ -466,18 +462,14 @@
 
 // Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
 // compensated start time.
-void LogKlog::synchronize(const char *buf, size_t len) {
-    const char *cp = strnstr(buf, len, suspendStr);
+void LogKlog::synchronize(const char* buf, size_t len) {
+    const char* cp = android::strnstr(buf, len, suspendStr);
     if (!cp) {
-        cp = strnstr(buf, len, resumeStr);
-        if (!cp) {
-            return;
-        }
+        cp = android::strnstr(buf, len, resumeStr);
+        if (!cp) return;
     } else {
-        const char *rp = strnstr(buf, len, resumeStr);
-        if (rp && (rp < cp)) {
-            cp = rp;
-        }
+        const char* rp = android::strnstr(buf, len, resumeStr);
+        if (rp && (rp < cp)) cp = rp;
     }
 
     do {
@@ -491,7 +483,7 @@
     log_time now;
     sniffTime(now, &cp, len - (cp - buf), true);
 
-    const char *suspended = strnstr(buf, len, suspendedStr);
+    const char* suspended = android::strnstr(buf, len, suspendedStr);
     if (!suspended || (suspended > cp)) {
         return;
     }
@@ -581,12 +573,12 @@
 //  logd.klogd:
 // return -1 if message logd.klogd: <signature>
 //
-int LogKlog::log(const char *buf, size_t len) {
-    if (auditd && strnstr(buf, len, " audit(")) {
+int LogKlog::log(const char* buf, size_t len) {
+    if (auditd && android::strnstr(buf, len, " audit(")) {
         return 0;
     }
 
-    const char *p = buf;
+    const char* p = buf;
     int pri = parseKernelPrio(&p, len);
 
     log_time now;
@@ -594,7 +586,7 @@
 
     // sniff for start marker
     const char klogd_message[] = "logd.klogd: ";
-    const char *start = strnstr(p, len - (p - buf), klogd_message);
+    const char* start = android::strnstr(p, len - (p - buf), klogd_message);
     if (start) {
         uint64_t sig = strtoll(start + sizeof(klogd_message) - 1, NULL, 10);
         if (sig == signature.nsec()) {
@@ -640,7 +632,7 @@
 
     static const char infoBrace[] = "[INFO]";
     static const size_t infoBraceLen = strlen(infoBrace);
-    if ((taglen >= infoBraceLen) && !fast<strncmp>(p, infoBrace, infoBraceLen)) {
+    if ((taglen >= infoBraceLen) && !fastcmp<strncmp>(p, infoBrace, infoBraceLen)) {
         // <PRI>[<TIME>] "[INFO]"<tag> ":" message
         bt = p + infoBraceLen;
         taglen -= infoBraceLen;
@@ -675,7 +667,7 @@
             p = cp + 1;
         } else if ((taglen > size) && (tolower(*bt) == tolower(*cp))) {
             // clean up any tag stutter
-            if (!fast<strncasecmp>(bt + 1, cp + 1, size - 1)) { // no match
+            if (!fastcmp<strncasecmp>(bt + 1, cp + 1, size - 1)) { // no match
                 // <PRI>[<TIME>] <tag> <tag> : message
                 // <PRI>[<TIME>] <tag> <tag>: message
                 // <PRI>[<TIME>] <tag> '<tag>.<num>' : message
@@ -697,8 +689,8 @@
                 static const char host[] = "_host";
                 static const size_t hostlen = strlen(host);
                 if ((size > hostlen) &&
-                        !fast<strncmp>(bt + size - hostlen, host, hostlen) &&
-                        !fast<strncmp>(bt + 1, cp + 1, size - hostlen - 1)) {
+                        !fastcmp<strncmp>(bt + size - hostlen, host, hostlen) &&
+                        !fastcmp<strncmp>(bt + 1, cp + 1, size - hostlen - 1)) {
                     const char *b = cp;
                     cp += size - hostlen;
                     taglen -= size - hostlen;
@@ -746,10 +738,10 @@
         // register names like x18 but not driver names like en0
             || ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2])))
         // blacklist
-            || ((size == cpuLen) && !fast<strncmp>(tag, cpu, cpuLen))
-            || ((size == warningLen) && !fast<strncasecmp>(tag, warning, warningLen))
-            || ((size == errorLen) && !fast<strncasecmp>(tag, error, errorLen))
-            || ((size == infoLen) && !fast<strncasecmp>(tag, info, infoLen))) {
+            || ((size == cpuLen) && !fastcmp<strncmp>(tag, cpu, cpuLen))
+            || ((size == warningLen) && !fastcmp<strncasecmp>(tag, warning, warningLen))
+            || ((size == errorLen) && !fastcmp<strncasecmp>(tag, error, errorLen))
+            || ((size == infoLen) && !fastcmp<strncasecmp>(tag, info, infoLen))) {
         p = start;
         etag = tag = "";
     }
@@ -761,7 +753,7 @@
     const char *mp = strnrchr(tag, ']', taglen);
     if (mp && (++mp < etag)) {
         size_t s = etag - mp;
-        if (((s + s) < taglen) && !fast<memcmp>(mp, mp - 1 - s, s)) {
+        if (((s + s) < taglen) && !fastcmp<memcmp>(mp, mp - 1 - s, s)) {
             taglen = mp - tag;
         }
     }
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 61d4c49..1b50b4e 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -108,7 +108,7 @@
     }
 
     bool nonBlock = false;
-    if (!fast<strncmp>(buffer, "dumpAndClose", 12)) {
+    if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) {
         // Allow writer to get some cycles, and wait for pending notifications
         sched_yield();
         LogTimeEntry::lock();
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index d4b48ef..7e0a6b7 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -27,6 +27,8 @@
 
 #include "LogStatistics.h"
 
+size_t LogStatistics::SizesTotal;
+
 LogStatistics::LogStatistics() : enable(false) {
     log_id_for_each(id) {
         mSizes[id] = 0;
@@ -39,6 +41,8 @@
 
 namespace android {
 
+size_t sizesTotal() { return LogStatistics::sizesTotal(); }
+
 // caller must own and free character string
 char *pidToName(pid_t pid) {
     char *retval = NULL;
@@ -53,7 +57,7 @@
             if (ret > 0) {
                 buffer[sizeof(buffer)-1] = '\0';
                 // frameworks intermediate state
-                if (fast<strcmp>(buffer, "<pre-initialized>")) {
+                if (fastcmp<strcmp>(buffer, "<pre-initialized>")) {
                     retval = strdup(buffer);
                 }
             }
@@ -71,8 +75,18 @@
     mSizes[log_id] += size;
     ++mElements[log_id];
 
-    mSizesTotal[log_id] += size;
-    ++mElementsTotal[log_id];
+    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];
+    }
 
     if (log_id == LOG_ID_KERNEL) {
         return;
@@ -182,7 +196,7 @@
     }
 
     // Parse /data/system/packages.list
-    uid_t userId = uid % AID_USER;
+    uid_t userId = uid % AID_USER_OFFSET;
     const char *name = android::uidToName(userId);
     if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) {
         name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP));
@@ -209,7 +223,7 @@
             if (nameTmp) {
                 if (!name) {
                     name = strdup(nameTmp);
-                } else if (fast<strcmp>(name, nameTmp)) {
+                } else if (fastcmp<strcmp>(name, nameTmp)) {
                     free(const_cast<char *>(name));
                     name = NULL;
                     break;
@@ -295,7 +309,7 @@
             if ((spaces <= 0) && pruned.length()) {
                 spaces = 1;
             }
-            if ((spaces > 0) && (pruned.length() != 0)) {
+            if (spaces > 0) {
                 change += android::base::StringPrintf("%*s", (int)spaces, "");
             }
             pruned = change + pruned;
@@ -438,12 +452,11 @@
         name = android::base::StringPrintf("%7u/%u",
                                            getKey(), uid);
     }
-    size_t len = 0;
-    const char *nameTmp = getName(len);
+    const char *nameTmp = getName();
     if (nameTmp) {
         name += android::base::StringPrintf(
-            "%*s%.*s", (int)std::max(14 - name.length(), (size_t)1),
-            "", (int)len, nameTmp);
+            "%*s%s", (int)std::max(14 - name.length(), (size_t)1),
+            "", nameTmp);
     }
 
     std::string size = android::base::StringPrintf("%zu",
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 1f598af..777dc33 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -17,11 +17,12 @@
 #ifndef _LOGD_LOG_STATISTICS_H__
 #define _LOGD_LOG_STATISTICS_H__
 
-#include <memory>
+#include <ctype.h>
 #include <stdlib.h>
 #include <sys/types.h>
 
 #include <algorithm> // std::max
+#include <memory>
 #include <string>    // std::string
 #include <unordered_map>
 
@@ -211,14 +212,16 @@
                                     EntryBaseConstants::total_len
                                         - name.length() - drop_len - 1);
 
-        if (pruned.length()) {
-            return android::base::StringPrintf("%s%*s%*s\n", name.c_str(),
-                                               (int)size_len, size.c_str(),
-                                               (int)drop_len, pruned.c_str());
-        } else {
-            return android::base::StringPrintf("%s%*s\n", name.c_str(),
-                                               (int)size_len, size.c_str());
-        }
+        std::string ret = android::base::StringPrintf("%s%*s%*s",
+                                                      name.c_str(),
+                                                      (int)size_len, size.c_str(),
+                                                      (int)drop_len, pruned.c_str());
+        // remove any trailing spaces
+        size_t pos = ret.size();
+        size_t len = 0;
+        while (pos && isspace(ret[--pos])) ++len;
+        if (len) ret.erase(pos + 1, len);
+        return ret + "\n";
     }
 };
 
@@ -265,7 +268,7 @@
         if (pid != element->getPid()) {
             pid = -1;
         }
-        EntryBase::add(element);
+        EntryBaseDropped::add(element);
     }
 
     std::string formatHeader(const std::string &name, log_id_t id) const;
@@ -307,7 +310,7 @@
     const char*getName() const { return name; }
 
     inline void add(pid_t newPid) {
-        if (name && !fast<strncmp>(name, "zygote", 6)) {
+        if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
             free(name);
             name = NULL;
         }
@@ -368,7 +371,7 @@
     const char*getName() const { return name; }
 
     inline void add(pid_t incomingTid) {
-        if (name && !fast<strncmp>(name, "zygote", 6)) {
+        if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
             free(name);
             name = NULL;
         }
@@ -410,7 +413,7 @@
     const uint32_t&getKey() const { return tag; }
     const pid_t&getPid() const { return pid; }
     const uid_t&getUid() const { return uid; }
-    const char*getName(size_t &len) const { return android::tagToName(&len, tag); }
+    const char*getName() const { return android::tagToName(tag); }
 
     inline void add(LogBufferElement *element) {
         if (uid != element->getUid()) {
@@ -419,7 +422,7 @@
         if (pid != element->getPid()) {
             pid = -1;
         }
-        EntryBase::add(element);
+        EntryBaseDropped::add(element);
     }
 
     std::string formatHeader(const std::string &name, log_id_t id) const;
@@ -472,6 +475,7 @@
     size_t mDroppedElements[LOG_ID_MAX];
     size_t mSizesTotal[LOG_ID_MAX];
     size_t mElementsTotal[LOG_ID_MAX];
+    static size_t SizesTotal;
     bool enable;
 
     // uid to size list
@@ -554,6 +558,7 @@
     }
     size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; }
     size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; }
+    static size_t sizesTotal() { return SizesTotal; }
 
     std::string format(uid_t uid, pid_t pid, unsigned int logMask) const;
 
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
new file mode 100644
index 0000000..64aa219
--- /dev/null
+++ b/logd/LogTags.cpp
@@ -0,0 +1,889 @@
+/*
+ * 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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <log/log_event_list.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "LogTags.h"
+#include "LogUtils.h"
+
+static LogTags* logtags;
+
+const char LogTags::system_event_log_tags[] = "/system/etc/event-log-tags";
+const char LogTags::dynamic_event_log_tags[] = "/dev/event-log-tags";
+// Only for debug
+const char LogTags::debug_event_log_tags[] = "/data/misc/logd/event-log-tags";
+
+// Sniff for first uid=%d in utf8z comment string
+static uid_t sniffUid(const char* comment, const char* endp) {
+    if (!comment) return AID_ROOT;
+
+    if (*comment == '#') ++comment;
+    while ((comment < endp) && (*comment != '\n') && isspace(*comment)) ++comment;
+    static const char uid_str[] = "uid=";
+    if (((comment + strlen(uid_str)) >= endp) ||
+            fastcmp<strncmp>(comment, uid_str, strlen(uid_str)) ||
+            !isdigit(comment[strlen(uid_str)])) return AID_ROOT;
+    char* cp;
+    unsigned long Uid = strtoul(comment + 4, &cp, 10);
+    if ((cp > endp) || (Uid >= INT_MAX)) return AID_ROOT;
+
+    return Uid;
+}
+
+// Checks for file corruption, and report false if there was no need
+// to rebuild the referenced file.  Failure to rebuild is only logged,
+// does not cause a return value of false.
+bool LogTags::RebuildFileEventLogTags(const char* filename, bool warn) {
+    int fd;
+
+    {
+        android::RWLock::AutoRLock readLock(rwlock);
+
+        if (tag2total.begin() == tag2total.end()) {
+            return false;
+        }
+
+        file2watermark_const_iterator iwater = file2watermark.find(filename);
+        if (iwater == file2watermark.end()) {
+            return false;
+        }
+
+        struct stat sb;
+        if (!stat(filename, &sb) && ((size_t)sb.st_size >= iwater->second)) {
+            return false;
+        }
+
+        // dump what we already know back into the file?
+        fd = TEMP_FAILURE_RETRY(open(filename,
+                                     O_WRONLY | O_TRUNC | O_CLOEXEC |
+                                     O_NOFOLLOW | O_BINARY));
+        if (fd >= 0) {
+            time_t now = time(NULL);
+            struct tm tm;
+            localtime_r(&now, &tm);
+            char timebuf[20];
+            size_t len = strftime(timebuf, sizeof(timebuf),
+                                  "%Y-%m-%d %H:%M:%S", &tm);
+            android::base::WriteStringToFd(
+                android::base::StringPrintf(
+                    "# Rebuilt %.20s, content owned by logd\n", timebuf),
+                fd);
+            for (const auto& it : tag2total) {
+                android::base::WriteStringToFd(formatEntry_locked(it.first,
+                                                                  AID_ROOT),
+                                               fd);
+            }
+            TEMP_FAILURE_RETRY(close(fd));
+        }
+    }
+
+    if (warn) {
+        android::prdebug(((fd < 0) ?
+                              "%s failed to rebuild" :
+                              "%s missing, damaged or truncated; rebuilt"),
+                         filename);
+    }
+
+    if (fd >= 0) {
+        android::RWLock::AutoWLock writeLock(rwlock);
+
+        struct stat sb;
+        if (!stat(filename, &sb)) file2watermark[filename] = sb.st_size;
+    }
+
+    return true;
+}
+
+void LogTags::AddEventLogTags(uint32_t tag, uid_t uid,
+                              const std::string& Name,
+                              const std::string& Format,
+                              const char* source, bool warn) {
+    std::string Key = Name;
+    if (Format.length()) Key += "+" + Format;
+
+    bool update = !source || !!strcmp(source, system_event_log_tags);
+    bool newOne;
+
+    {
+        android::RWLock::AutoWLock writeLock(rwlock);
+
+        tag2total_const_iterator itot = tag2total.find(tag);
+
+        // unlikely except for dupes, or updates to uid list (more later)
+        if (itot != tag2total.end()) update = false;
+
+        newOne = tag2name.find(tag) == tag2name.end();
+        key2tag[Key] = tag;
+
+        if (Format.length()) {
+            if (key2tag.find(Name) == key2tag.end()) {
+                key2tag[Name] = tag;
+            }
+            tag2format[tag] = Format;
+        }
+        tag2name[tag] = Name;
+
+        tag2uid_const_iterator ut = tag2uid.find(tag);
+        if (ut != tag2uid.end()) {
+            if (uid == AID_ROOT) {
+                tag2uid.erase(ut);
+                update = true;
+            } else if (ut->second.find(uid) == ut->second.end()) {
+                const_cast<uid_list&>(ut->second).emplace(uid);
+                update = true;
+            }
+        } else if (newOne && (uid != AID_ROOT)) {
+            tag2uid[tag].emplace(uid);
+            update = true;
+        }
+
+        // updatePersist -> trigger output on modified
+        // content, reset tag2total if available
+        if (update && (itot != tag2total.end())) tag2total[tag] = 0;
+    }
+
+    if (update) {
+        WritePersistEventLogTags(tag, uid, source);
+    } else if (warn && !newOne && source) {
+        // For the files, we want to report dupes.
+        android::prdebug("Multiple tag %" PRIu32 " %s %s %s", tag,
+            Name.c_str(), Format.c_str(), source);
+    }
+}
+
+// Read the event log tags file, and build up our internal database
+void LogTags::ReadFileEventLogTags(const char* filename, bool warn) {
+    bool etc = !strcmp(filename, system_event_log_tags);
+    bool debug = !etc && !strcmp(filename, debug_event_log_tags);
+
+    if (!etc) {
+        RebuildFileEventLogTags(filename, warn);
+    }
+    std::string content;
+    if (android::base::ReadFileToString(filename, &content)) {
+        char* cp = (char*) content.c_str();
+        char* endp = cp + content.length();
+
+        {
+            android::RWLock::AutoRLock writeLock(rwlock);
+
+            file2watermark[filename] = content.length();
+        }
+
+        char* lineStart = cp;
+        while (cp < endp) {
+            if (*cp == '\n') {
+                lineStart = cp;
+            } else if (lineStart) {
+                if (*cp == '#') {
+                    /* comment; just scan to end */
+                    lineStart = NULL;
+                } else if (isdigit(*cp)) {
+                    unsigned long Tag = strtoul(cp, &cp, 10);
+                    if (warn && (Tag > emptyTag)) {
+                        android::prdebug("tag too large %lu", Tag);
+                    }
+                    while ((cp < endp) && (*cp != '\n') && isspace(*cp)) ++cp;
+                    if (cp >= endp) break;
+                    if (*cp == '\n') continue;
+                    const char* name = cp;
+                    /* Determine whether it is a valid tag name [a-zA-Z0-9_] */
+                    bool hasAlpha = false;
+                    while ((cp < endp) && (isalnum(*cp) || (*cp == '_'))) {
+                        if (!isdigit(*cp)) hasAlpha = true;
+                        ++cp;
+                    }
+                    std::string Name(name, cp - name);
+#ifdef ALLOW_NOISY_LOGGING_OF_PROBLEM_WITH_LOTS_OF_TECHNICAL_DEBT
+                    static const size_t maximum_official_tag_name_size = 24;
+                    if (warn && (Name.length() > maximum_official_tag_name_size)) {
+                       android::prdebug("tag name too long %s", Name.c_str());
+                    }
+#endif
+                    if (hasAlpha && ((cp >= endp) || (*cp == '#') || isspace(*cp))) {
+                        if (Tag > emptyTag) {
+                            if (*cp != '\n') lineStart = NULL;
+                            continue;
+                        }
+                        while ((cp < endp) && (*cp != '\n') && isspace(*cp)) ++cp;
+                        const char* format = cp;
+                        uid_t uid = AID_ROOT;
+                        while ((cp < endp) && (*cp != '\n')) {
+                            if (*cp == '#') {
+                                uid = sniffUid(cp, endp);
+                                lineStart = NULL;
+                                break;
+                            }
+                            ++cp;
+                        }
+                        while ((cp > format) && isspace(cp[-1])) {
+                            --cp;
+                            lineStart = NULL;
+                        }
+                        std::string Format(format, cp - format);
+
+                        AddEventLogTags((uint32_t)Tag, uid, Name, Format,
+                                        filename, warn);
+                    } else {
+                        if (warn) {
+                            android::prdebug("tag name invalid %.*s",
+                                             (int)(cp - name + 1), name);
+                        }
+                        lineStart = NULL;
+                    }
+                } else if (!isspace(*cp)) break;
+            }
+            cp++;
+        }
+    } else if (warn) {
+        android::prdebug("Cannot read %s", filename);
+    }
+}
+
+// Extract a 4-byte value from a byte stream.
+static inline uint32_t get4LE(const char* msg)
+{
+    const uint8_t* src = reinterpret_cast<const uint8_t*>(msg);
+    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+// Additional persistent sources for invented log tags.  Read the
+// special pmsg event for log tags, and build up our internal
+// database with any found.
+void LogTags::ReadPersistEventLogTags() {
+    struct logger_list* logger_list = android_logger_list_alloc(
+        ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK,
+        0, (pid_t)0);
+    if (!logger_list) return;
+
+    struct logger* e = android_logger_open(logger_list, LOG_ID_EVENTS);
+    struct logger* s = android_logger_open(logger_list, LOG_ID_SECURITY);
+    if (!e && !s) {
+        android_logger_list_free(logger_list);
+        return;
+    }
+
+    for (;;) {
+        struct log_msg log_msg;
+        int ret = android_logger_list_read(logger_list, &log_msg);
+        if (ret <= 0) break;
+
+        const char* msg = log_msg.msg();
+        if (!msg) continue;
+        if (log_msg.entry.len <= sizeof(uint32_t)) continue;
+        uint32_t Tag = get4LE(msg);
+        if (Tag != TAG_DEF_LOG_TAG) continue;
+        uid_t uid = (log_msg.entry.hdr_size >= sizeof(logger_entry_v4)) ?
+            log_msg.entry.uid : AID_ROOT;
+
+        std::string Name;
+        std::string Format;
+        android_log_list_element elem;
+        {
+            android_log_event_list ctx(log_msg);
+            elem = ctx.read();
+            if (elem.type != EVENT_TYPE_LIST) {
+                continue;
+            }
+            elem = ctx.read();
+            if (elem.type != EVENT_TYPE_INT) {
+                continue;
+            }
+            Tag = elem.data.int32;
+            elem = ctx.read();
+            if (elem.type != EVENT_TYPE_STRING) {
+                continue;
+            }
+            Name = std::string(elem.data.string, elem.len);
+            elem = ctx.read();
+            if (elem.type != EVENT_TYPE_STRING) {
+                continue;
+            }
+            Format = std::string(elem.data.string, elem.len);
+            elem = ctx.read();
+        }
+        if ((elem.type != EVENT_TYPE_LIST_STOP) || !elem.complete) continue;
+
+        AddEventLogTags(Tag, uid, Name, Format);
+    }
+    android_logger_list_free(logger_list);
+}
+
+LogTags::LogTags() {
+    ReadFileEventLogTags(system_event_log_tags);
+    // Following will likely fail on boot, but is required if logd restarts
+    ReadFileEventLogTags(dynamic_event_log_tags, false);
+    if (__android_log_is_debuggable()) {
+        ReadFileEventLogTags(debug_event_log_tags, false);
+    }
+    ReadPersistEventLogTags();
+
+    logtags = this;
+}
+
+// Converts an event tag into a name
+const char* LogTags::tagToName(uint32_t tag) const {
+    tag2name_const_iterator it;
+
+    android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+    it = tag2name.find(tag);
+    if ((it == tag2name.end()) || (it->second.length() == 0)) return NULL;
+
+    return it->second.c_str();
+}
+
+// Prototype in LogUtils.h allowing external access to our database.
+//
+// This must be a pure reader to our database, as everything else is
+// guaranteed single-threaded except this access point which is
+// asynchonous and can be multithreaded and thus rentrant.  The
+// object's rwlock is only used to guarantee atomic access to the
+// unordered_map to prevent corruption, with a requirement to be a
+// low chance of contention for this call.  If we end up changing
+// this algorithm resulting in write, then we should use a different
+// lock than the object's rwlock to protect groups of associated
+// actions.
+const char* android::tagToName(uint32_t tag) {
+    LogTags* me = logtags;
+
+    if (!me) return NULL;
+    me->WritePmsgEventLogTags(tag);
+    return me->tagToName(tag);
+}
+
+// Prototype in LogUtils.h allowing external access to our database.
+//
+// This only works on userdebug and eng devices to re-read the
+// /data/misc/logd/event-log-tags file right after /data is mounted.
+// The operation is near to boot and should only happen once.  There
+// are races associated with its use since it can trigger a Rebuild
+// of the file, but that is a can-not-happen since the file was not
+// read yet.  More dangerous if called later, but if all is well it
+// should just skip over everything and not write any new entries.
+void android::ReReadEventLogTags() {
+    LogTags* me = logtags;
+
+    if (me && __android_log_is_debuggable()) {
+        me->ReadFileEventLogTags(me->debug_event_log_tags);
+    }
+}
+
+// converts an event tag into a format
+const char* LogTags::tagToFormat(uint32_t tag) const {
+    tag2format_const_iterator iform;
+
+    android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+    iform = tag2format.find(tag);
+    if (iform == tag2format.end()) return NULL;
+
+    return iform->second.c_str();
+}
+
+// converts a name into an event tag
+uint32_t LogTags::nameToTag(const char* name) const {
+    uint32_t ret = emptyTag;
+
+    // Bug: Only works for a single entry, we can have multiple entries,
+    // one for each format, so we find first entry recorded, or entry with
+    // no format associated with it.
+
+    android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+    key2tag_const_iterator ik = key2tag.find(std::string(name));
+    if (ik != key2tag.end()) ret = ik->second;
+
+    return ret;
+}
+
+// Caller must perform locks, can be under reader (for pre-check) or
+// writer lock.  We use this call to invent a new deterministically
+// random tag, unique is cleared if no conflicts.  If format is NULL,
+// we are in readonly mode.
+uint32_t LogTags::nameToTag_locked(const std::string& name,
+                                   const char* format,
+                                   bool& unique) {
+    key2tag_const_iterator ik;
+
+    bool write = format != NULL;
+    unique = write;
+
+    if (!write) {
+        // Bug: Only works for a single entry, we can have multiple entries,
+        // one for each format, so we find first entry recorded, or entry with
+        // no format associated with it.
+        ik = key2tag.find(name);
+        if (ik == key2tag.end()) return emptyTag;
+        return ik->second;
+    }
+
+    std::string Key(name);
+    if (*format) Key += std::string("+") + format;
+
+    ik = key2tag.find(Key);
+    if (ik != key2tag.end()) {
+        unique = false;
+        return ik->second;
+    }
+
+    size_t Hash = key2tag.hash_function()(Key);
+    uint32_t Tag = Hash;
+    // This sets an upper limit on the conflics we are allowed to deal with.
+    for (unsigned i = 0; i < 256; ) {
+        tag2name_const_iterator it = tag2name.find(Tag);
+        if (it == tag2name.end()) return Tag;
+        std::string localKey(it->second);
+        tag2format_const_iterator iform = tag2format.find(Tag);
+        if ((iform == tag2format.end()) && iform->second.length()) {
+            localKey += "+" + iform->second;
+        }
+        unique = !!it->second.compare(localKey);
+        if (!unique) return Tag; // unlikely except in a race
+
+        ++i;
+        // Algorithm to convert hash to next tag
+        if (i < 32) {
+            Tag = (Hash >> i);
+            // size_t is 32 bits, or upper word zero, rotate
+            if ((sizeof(Hash) <= 4) ||
+                    ((Hash & (uint64_t(-1LL) << 32)) == 0)) {
+                Tag |= Hash << (32 - i);
+            }
+        } else {
+            Tag = Hash + i - 31;
+        }
+    }
+    return emptyTag;
+}
+
+static int openFile(const char* name, int mode, bool warning) {
+    int fd = TEMP_FAILURE_RETRY(open(name, mode));
+    if ((fd < 0) && warning) {
+        android::prdebug("Failed open %s (%d)", name, errno);
+    }
+    return fd;
+}
+
+void LogTags::WritePmsgEventLogTags(uint32_t tag, uid_t uid) {
+    android::RWLock::AutoRLock readLock(rwlock);
+
+    tag2total_const_iterator itot = tag2total.find(tag);
+    if (itot == tag2total.end()) return; // source is a static entry
+
+    size_t lastTotal = itot->second;
+
+    // Every 16K (half the smallest configurable pmsg buffer size) record
+    static const size_t rate_to_pmsg = 16 * 1024;
+    if (lastTotal && ((android::sizesTotal() - lastTotal) < rate_to_pmsg)) {
+        return;
+    }
+
+    static int pmsg_fd = -1;
+    if (pmsg_fd < 0) {
+        pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+        // unlikely, but deal with partners with borken pmsg
+        if (pmsg_fd < 0) return;
+    }
+
+    std::string Name = tag2name[tag];
+    tag2format_const_iterator iform = tag2format.find(tag);
+    std::string Format = (iform != tag2format.end()) ? iform->second : "";
+
+    __android_log_event_list ctx(TAG_DEF_LOG_TAG);
+    ctx << tag << Name << Format;
+    std::string buffer(ctx);
+    if (buffer.length() <= 0) return; // unlikely
+
+    /*
+     *  struct {
+     *      // what we provide to pstore
+     *      android_pmsg_log_header_t pmsgHeader;
+     *      // what we provide to file
+     *      android_log_header_t header;
+     *      // caller provides
+     *      union {
+     *          struct {
+     *              char     prio;
+     *              char     payload[];
+     *          } string;
+     *          struct {
+     *              uint32_t tag
+     *              char     payload[];
+     *          } binary;
+     *      };
+     *  };
+     */
+
+    struct timespec ts;
+    clock_gettime(android_log_clockid(), &ts);
+
+    android_log_header_t header = {
+        .id = LOG_ID_EVENTS,
+        .tid = (uint16_t)gettid(),
+        .realtime.tv_sec = (uint32_t)ts.tv_sec,
+        .realtime.tv_nsec = (uint32_t)ts.tv_nsec,
+    };
+
+    uint32_t outTag = TAG_DEF_LOG_TAG;
+    outTag = get4LE((const char*)&outTag);
+
+    android_pmsg_log_header_t pmsgHeader = {
+        .magic = LOGGER_MAGIC,
+        .len = (uint16_t)(sizeof(pmsgHeader) + sizeof(header) +
+                          sizeof(outTag) + buffer.length()),
+        .uid = (uint16_t)AID_ROOT,
+        .pid = (uint16_t)getpid(),
+    };
+
+    struct iovec Vec[] = {
+        { (unsigned char*)&pmsgHeader, sizeof(pmsgHeader) },
+        { (unsigned char*)&header, sizeof(header) },
+        { (unsigned char*)&outTag, sizeof(outTag) },
+        { (unsigned char*)const_cast<char*>(buffer.data()), buffer.length() }
+    };
+
+    tag2uid_const_iterator ut = tag2uid.find(tag);
+    if (ut == tag2uid.end()) {
+        TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+    } else if (uid != AID_ROOT) {
+        pmsgHeader.uid = (uint16_t)uid;
+        TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+    } else {
+        for (auto &it : ut->second) {
+            pmsgHeader.uid = (uint16_t)it;
+            TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+        }
+    }
+}
+
+void LogTags::WriteDynamicEventLogTags(uint32_t tag, uid_t uid) {
+    static const int mode = O_WRONLY | O_APPEND |
+                            O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+
+    int fd = openFile(dynamic_event_log_tags, mode, true);
+    if (fd < 0) return;
+
+    android::RWLock::AutoWLock writeLock(rwlock);
+
+    std::string ret = formatEntry_locked(tag, uid, false);
+    android::base::WriteStringToFd(ret, fd);
+    TEMP_FAILURE_RETRY(close(fd));
+
+    size_t size = 0;
+    file2watermark_const_iterator iwater;
+
+    iwater = file2watermark.find(dynamic_event_log_tags);
+    if (iwater != file2watermark.end()) size = iwater->second;
+
+    file2watermark[dynamic_event_log_tags] = size + ret.length();
+}
+
+void LogTags::WriteDebugEventLogTags(uint32_t tag, uid_t uid) {
+    static const int mode = O_WRONLY | O_APPEND |
+                            O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+
+    static bool one = true;
+    int fd = openFile(debug_event_log_tags, mode, one);
+    one = fd >= 0;
+    if (!one) return;
+
+    android::RWLock::AutoWLock writeLock(rwlock);
+
+    std::string ret = formatEntry_locked(tag, uid, false);
+    android::base::WriteStringToFd(ret, fd);
+    TEMP_FAILURE_RETRY(close(fd));
+
+    size_t size = 0;
+    file2watermark_const_iterator iwater;
+
+    iwater = file2watermark.find(debug_event_log_tags);
+    if (iwater != file2watermark.end()) size = iwater->second;
+
+    file2watermark[debug_event_log_tags] = size + ret.length();
+}
+
+// How we maintain some runtime or reboot stickiness
+void LogTags::WritePersistEventLogTags(uint32_t tag,
+                                       uid_t uid, const char* source) {
+    // very unlikely
+    bool etc = source && !strcmp(source, system_event_log_tags);
+    if (etc) return;
+
+    bool dynamic = source && !strcmp(source, dynamic_event_log_tags);
+    bool debug = (!dynamic &&
+                  source &&
+                  !strcmp(source, debug_event_log_tags)) ||
+                 !__android_log_is_debuggable();
+
+    WritePmsgEventLogTags(tag, uid);
+
+    size_t lastTotal = 0;
+    {
+        android::RWLock::AutoRLock readLock(rwlock);
+
+        tag2total_const_iterator itot = tag2total.find(tag);
+        if (itot != tag2total.end()) lastTotal = itot->second;
+    }
+
+    if (lastTotal == 0) { // denotes first time for this one
+        if (!dynamic || !RebuildFileEventLogTags(dynamic_event_log_tags)) {
+            WriteDynamicEventLogTags(tag, uid);
+        }
+
+        if (!debug && !RebuildFileEventLogTags(debug_event_log_tags)) {
+            WriteDebugEventLogTags(tag, uid);
+        }
+    }
+
+    lastTotal = android::sizesTotal();
+    if (!lastTotal) ++lastTotal;
+
+    // record totals for next watermark.
+    android::RWLock::AutoWLock writeLock(rwlock);
+    tag2total[tag] = lastTotal;
+}
+
+// nameToTag converts a name into an event tag. If format is NULL, then we
+// are in readonly mode.
+uint32_t LogTags::nameToTag(uid_t uid, const char* name, const char* format) {
+    std::string Name = std::string(name);
+    bool write = format != NULL;
+    bool updateUid = uid != AID_ROOT;
+    bool updateFormat = format && *format;
+    bool unique;
+    uint32_t Tag;
+
+    {
+        android::RWLock::AutoRLock readLock(rwlock);
+
+        Tag = nameToTag_locked(Name, format, unique);
+        if (updateUid && (Tag != emptyTag) && !unique) {
+            tag2uid_const_iterator ut = tag2uid.find(Tag);
+            if (updateUid) {
+                if ((ut != tag2uid.end()) &&
+                        (ut->second.find(uid) == ut->second.end())) {
+                    unique = write; // write passthrough to update uid counts
+                    if (!write) Tag = emptyTag; // deny read access
+                }
+            } else {
+                unique = write && (ut != tag2uid.end());
+            }
+        }
+    }
+
+    if (Tag == emptyTag) return Tag;
+    WritePmsgEventLogTags(Tag, uid); // record references periodically
+    if (!unique) return Tag;
+
+    bool updateWrite = false;
+    bool updateTag;
+
+    // Special case of AddEventLogTags, checks per-uid counter which makes
+    // no sense there, and is also optimized somewhat to reduce write times.
+    {
+        android::RWLock::AutoWLock writeLock(rwlock);
+
+        // double check after switch from read lock to write lock for Tag
+        updateTag = tag2name.find(Tag) == tag2name.end();
+        // unlikely, either update, race inviting conflict or multiple uids
+        if (!updateTag) {
+            Tag = nameToTag_locked(Name, format, unique);
+            if (Tag == emptyTag) return Tag;
+            // is it multiple uid's setting this value
+            if (!unique) {
+                tag2uid_const_iterator ut = tag2uid.find(Tag);
+                if (updateUid) {
+                    // Add it to the uid list
+                    if ((ut == tag2uid.end()) ||
+                        (ut->second.find(uid) != ut->second.end())) return Tag;
+                    const_cast<uid_list&>(ut->second).emplace(uid);
+                    updateWrite = true;
+                } else {
+                    if (ut == tag2uid.end()) return Tag;
+                    // (system) adding a global one, erase the uid list
+                    tag2uid.erase(ut);
+                    updateWrite = true;
+                }
+            }
+        }
+
+        // Update section
+        size_t count;
+        if (updateUid) {
+            count = 0;
+            uid2count_const_iterator ci = uid2count.find(uid);
+            if (ci != uid2count.end()) {
+                count = ci->second;
+                if (count >= max_per_uid) {
+                    if (!updateWrite) return emptyTag;
+                    // If we are added to the per-Uid perms, leak the Tag
+                    // if it already exists.
+                    updateUid = false;
+                    updateTag = false;
+                    updateFormat = false;
+                }
+            }
+        }
+
+        // updateWrite -> trigger output on modified content, reset tag2total
+        //    also sets static to dynamic entries if they are alterred,
+        //    only occurs if they have a uid, and runtime adds another uid.
+        if (updateWrite) tag2total[Tag] = 0;
+
+        if (updateTag) {
+            // mark as a dynamic entry, but do not upset current total counter
+            tag2total_const_iterator itot = tag2total.find(Tag);
+            if (itot == tag2total.end()) tag2total[Tag] = 0;
+
+            if (*format) {
+                key2tag[Name + "+" + format] = Tag;
+                if (key2tag.find(Name) == key2tag.end()) key2tag[Name] = Tag;
+            } else {
+                key2tag[Name] = Tag;
+            }
+            tag2name[Tag] = Name;
+        }
+        if (updateFormat) tag2format[Tag] = format;
+
+        if (updateUid) {
+            tag2uid[Tag].emplace(uid);
+            uid2count[uid] = count + 1;
+        }
+    }
+
+    if (updateTag || updateFormat || updateWrite) {
+        WritePersistEventLogTags(Tag, uid);
+    }
+
+    return Tag;
+}
+
+std::string LogTags::formatEntry(uint32_t tag, uid_t uid,
+                                 const char* name,
+                                 const char* format) {
+    if (!format || !format[0]) {
+        return android::base::StringPrintf("%" PRIu32 "\t%s\n", tag, name);
+    }
+    size_t len = (strlen(name) + 7) / 8;
+    static const char tabs[] = "\t\t\t";
+    if (len > strlen(tabs)) len = strlen(tabs);
+    std::string Uid;
+    if (uid != AID_ROOT) Uid = android::base::StringPrintf(" # uid=%u", uid);
+    return android::base::StringPrintf("%" PRIu32 "\t%s%s\t%s%s\n",
+                                       tag, name, &tabs[len], format,
+                                       Uid.c_str());
+}
+
+std::string LogTags::formatEntry_locked(uint32_t tag, uid_t uid,
+                                        bool authenticate) {
+    const char* name = tag2name[tag].c_str();
+
+    const char* format = "";
+    tag2format_const_iterator iform = tag2format.find(tag);
+    if (iform != tag2format.end()) format = iform->second.c_str();
+
+    // Access permission test, do not report dynamic entries
+    // that do not belong to us.
+    tag2uid_const_iterator ut = tag2uid.find(tag);
+    if (ut == tag2uid.end()) {
+        return formatEntry(tag, AID_ROOT, name, format);
+    }
+    if (uid != AID_ROOT) {
+        if (authenticate && (ut->second.find(uid) == ut->second.end())) {
+            return std::string("");
+        }
+        return formatEntry(tag, uid, name, format);
+    }
+
+    // Show all, one for each registered uid (we are group root)
+    std::string ret;
+    for (auto &it : ut->second) {
+        ret += formatEntry(tag, it, name, format);
+    }
+    return ret;
+}
+
+std::string LogTags::formatEntry(uint32_t tag, uid_t uid) {
+    android::RWLock::AutoRLock readLock(rwlock);
+    return formatEntry_locked(tag, uid);
+}
+
+std::string LogTags::formatGetEventTag(uid_t uid,
+                                       const char* name, const char* format) {
+    bool all = name && (name[0] == '*') && !name[1];
+    bool list = !name || all;
+    std::string ret;
+
+    if (!list) {
+        // switch to read entry only if format == "*"
+        if (format && (format[0] == '*') && !format[1]) format = NULL;
+
+        // WAI: for null format, only works for a single entry, we can have
+        // multiple entries, one for each format, so we find first entry
+        // recorded, or entry with no format associated with it.
+        // We may desire to print all that match the name, but we did not
+        // add a mapping table for that and the cost is too high.
+        uint32_t tag = nameToTag(uid, name, format);
+        if (tag == emptyTag) return std::string("-1 ESRCH");
+        if (uid == AID_ROOT) {
+            android::RWLock::AutoRLock readLock(rwlock);
+
+            // first uid in list so as to manufacture an accurate reference
+            tag2uid_const_iterator ut = tag2uid.find(tag);
+            if ((ut != tag2uid.end()) &&
+                 (ut->second.begin() != ut->second.end())) {
+                uid = *(ut->second.begin());
+            }
+        }
+        ret = formatEntry(tag, uid, name, format ?: tagToFormat(tag));
+        if (!ret.length()) return std::string("-1 ESRCH");
+        return ret;
+    }
+
+    android::RWLock::AutoRLock readLock(rwlock);
+    if (all) {
+        // everything under the sun
+        for (const auto& it : tag2name) {
+            ret += formatEntry_locked(it.first, uid);
+        }
+    } else {
+        // set entries are dynamic
+        for (const auto& it : tag2total) {
+            ret += formatEntry_locked(it.first, uid);
+        }
+    }
+    return ret;
+}
diff --git a/logd/LogTags.h b/logd/LogTags.h
new file mode 100644
index 0000000..4457c46
--- /dev/null
+++ b/logd/LogTags.h
@@ -0,0 +1,119 @@
+/*
+ * 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 _LOGD_LOG_TAGS_H__
+#define _LOGD_LOG_TAGS_H__
+
+#include <unordered_map>
+#include <unordered_set>
+#include <string>
+
+#include <utils/RWLock.h>
+
+class LogTags {
+    // This lock protects all the unordered_map accesses below.  It
+    // is a reader/writer lock so that contentions are kept to a
+    // minimum since writes are rare, even administratably when
+    // reads are extended.  Resist the temptation to use the writer
+    // lock to protect anything outside the following unordered_maps
+    // as that would increase the reader contentions.  Use a separate
+    // mutex to protect the other entities.
+    android::RWLock rwlock;
+
+    // key is Name + "+" + Format
+    std::unordered_map<std::string, uint32_t> key2tag;
+    typedef std::unordered_map<std::string, uint32_t>::const_iterator key2tag_const_iterator;
+
+    // Allows us to manage access permissions based on uid registrants
+    // Global entries are specifically erased.
+    typedef std::unordered_set<uid_t> uid_list;
+    std::unordered_map<uint32_t, uid_list> tag2uid;
+    typedef std::unordered_map<uint32_t, uid_list>::const_iterator tag2uid_const_iterator;
+
+    std::unordered_map<uint32_t, std::string> tag2name;
+    typedef std::unordered_map<uint32_t, std::string>::const_iterator tag2name_const_iterator;
+
+    std::unordered_map<uint32_t, std::string> tag2format;
+    typedef std::unordered_map<uint32_t, std::string>::const_iterator tag2format_const_iterator;
+
+    static const size_t max_per_uid = 256; // Put a cap on the tags per uid
+    std::unordered_map<uid_t, size_t> uid2count;
+    typedef std::unordered_map<uid_t, size_t>::const_iterator uid2count_const_iterator;
+
+    // Dynamic entries are assigned
+    std::unordered_map<uint32_t, size_t> tag2total;
+    typedef std::unordered_map<uint32_t, size_t>::const_iterator tag2total_const_iterator;
+
+    // emplace unique tag
+    uint32_t nameToTag(uid_t uid, const char* name, const char* format);
+    // find unique or associated tag
+    uint32_t nameToTag_locked(const std::string& name, const char* format, bool &unique);
+
+    // Record expected file watermarks to detect corruption.
+    std::unordered_map<std::string, size_t> file2watermark;
+    typedef std::unordered_map<std::string, size_t>::const_iterator file2watermark_const_iterator;
+
+    void ReadPersistEventLogTags();
+
+    // format helpers
+    // format a single entry, does not need object data
+    static std::string formatEntry(uint32_t tag, uid_t uid,
+                                   const char* name, const char* format);
+    // caller locks, database lookup, authenticate against uid
+    std::string formatEntry_locked(uint32_t tag, uid_t uid,
+                                   bool authenticate = true);
+
+    bool RebuildFileEventLogTags(const char* filename, bool warn = true);
+
+    void AddEventLogTags(uint32_t tag, uid_t uid,
+                         const std::string& Name, const std::string& Format,
+                         const char* source = NULL, bool warn = false);
+
+    void WriteDynamicEventLogTags(uint32_t tag, uid_t uid);
+    void WriteDebugEventLogTags(uint32_t tag, uid_t uid);
+    // push tag details to persistent storage
+    void WritePersistEventLogTags(uint32_t tag,
+                                  uid_t uid = AID_ROOT,
+                                  const char* source = NULL);
+
+    static const uint32_t emptyTag = uint32_t(-1);
+
+public:
+
+    static const char system_event_log_tags[];
+    static const char dynamic_event_log_tags[];
+    // Only for userdebug and eng
+    static const char debug_event_log_tags[];
+
+    LogTags();
+
+    void WritePmsgEventLogTags(uint32_t tag, uid_t uid = AID_ROOT);
+    void ReadFileEventLogTags(const char* filename, bool warn = true);
+
+    // reverse lookup from tag
+    const char* tagToName(uint32_t tag) const;
+    const char* tagToFormat(uint32_t tag) const;
+    std::string formatEntry(uint32_t tag, uid_t uid);
+    // find associated tag
+    uint32_t nameToTag(const char* name) const;
+
+    // emplace tag if necessary, provide event-log-tag formated output in string
+    std::string formatGetEventTag(uid_t uid,
+                                  const char* name,
+                                  const char* format);
+};
+
+#endif // _LOGD_LOG_TAGS_H__
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index 44ac742..f044b27 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -20,6 +20,7 @@
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
+#include <utils/FastStrcmp.h>
 #include <private/android_logger.h>
 #include <sysutils/SocketClient.h>
 
@@ -32,12 +33,18 @@
 char *uidToName(uid_t uid);
 void prdebug(const char *fmt, ...) __printflike(1, 2);
 
-// Furnished in LogStatistics.cpp. Caller must own and free returned value
+// Furnished in LogStatistics.cpp.
+size_t sizesTotal();
+// Caller must own and free returned value
 char *pidToName(pid_t pid);
 char *tidToName(pid_t tid);
 
-// Furnished in main.cpp. Thread safe.
-const char *tagToName(size_t *len, uint32_t tag);
+// Furnished in LogTags.cpp. Thread safe.
+const char *tagToName(uint32_t tag);
+void ReReadEventLogTags();
+
+// Furnished by LogKlog.cpp.
+const char* strnstr(const char* s, size_t len, const char* needle);
 
 }
 
@@ -50,21 +57,4 @@
             (id == LOG_ID_RADIO) || (id == LOG_ID_EVENTS);
 }
 
-template <int (*cmp)(const char *l, const char *r, const size_t s)>
-static inline int fast(const char *l, const char *r, const size_t s) {
-    return (*l != *r) || cmp(l + 1, r + 1, s - 1);
-}
-
-template <int (*cmp)(const void *l, const void *r, const size_t s)>
-static inline int fast(const void *lv, const void *rv, const size_t s) {
-    const char *l = static_cast<const char *>(lv);
-    const char *r = static_cast<const char *>(rv);
-    return (*l != *r) || cmp(l + 1, r + 1, s - 1);
-}
-
-template <int (*cmp)(const char *l, const char *r)>
-static inline int fast(const char *l, const char *r) {
-    return (*l != *r) || cmp(l + 1, r + 1);
-}
-
 #endif // _LOGD_LOG_UTILS_H__
diff --git a/logd/README.property b/logd/README.property
index 791b1d5..de6767a 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -2,8 +2,9 @@
 
 name                       type default  description
 ro.logd.auditd             bool   true   Enable selinux audit daemon
-ro.logd.auditd.dmesg       bool   true   selinux audit messages duplicated and
-                                         sent on to dmesg log
+ro.logd.auditd.dmesg       bool   true   selinux audit messages sent to dmesg.
+ro.logd.auditd.main        bool   true   selinux audit messages sent to main.
+ro.logd.auditd.events      bool   true   selinux audit messages sent to events.
 persist.logd.security      bool   false  Enable security buffer.
 ro.device_owner            bool   false  Override persist.logd.security to false
 ro.logd.kernel             bool+ svelte+ Enable klogd daemon
diff --git a/logd/event.logtags b/logd/event.logtags
index 0d24df0..39063a9 100644
--- a/logd/event.logtags
+++ b/logd/event.logtags
@@ -35,3 +35,4 @@
 
 1003  auditd (avc|3)
 1004  chatty (dropped|3)
+1005  tag_def (tag|1),(name|3),(format|3)
diff --git a/logd/libaudit.c b/logd/libaudit.c
index d2b212e..216f1a1 100644
--- a/logd/libaudit.c
+++ b/logd/libaudit.c
@@ -149,7 +149,7 @@
     return rc;
 }
 
-int audit_setup(int fd, uint32_t pid)
+int audit_setup(int fd, pid_t pid)
 {
     int rc;
     struct audit_message rep;
@@ -163,8 +163,7 @@
      * and the the mask set to AUDIT_STATUS_PID
      */
     status.pid = pid;
-    status.mask = AUDIT_STATUS_PID | AUDIT_STATUS_RATE_LIMIT;
-    status.rate_limit = 20; // audit entries per second
+    status.mask = AUDIT_STATUS_PID;
 
     /* Let the kernel know this pid will be registering for audit events */
     rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
@@ -187,6 +186,27 @@
     return 0;
 }
 
+int audit_rate_limit(int fd, unsigned rate_limit)
+{
+    int rc;
+    struct audit_message rep;
+    struct audit_status status;
+
+    memset(&status, 0, sizeof(status));
+
+    status.mask = AUDIT_STATUS_RATE_LIMIT;
+    status.rate_limit = rate_limit; /* audit entries per second */
+
+    rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
+    if (rc < 0) {
+        return rc;
+    }
+
+    audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);
+
+    return 0;
+}
+
 int audit_open()
 {
     return socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT);
diff --git a/logd/libaudit.h b/logd/libaudit.h
index b9e330d..9865d43 100644
--- a/logd/libaudit.h
+++ b/logd/libaudit.h
@@ -33,7 +33,7 @@
 #define MAX_AUDIT_MESSAGE_LENGTH    8970
 
 typedef enum {
-    GET_REPLY_BLOCKING=0,
+    GET_REPLY_BLOCKING = 0,
     GET_REPLY_NONBLOCKING
 } reply_t;
 
@@ -55,7 +55,7 @@
  *  A valid fd on success or < 0 on error with errno set.
  *  Returns the same errors as man 2 socket.
  */
-extern int  audit_open(void);
+extern int audit_open(void);
 
 /**
  * Closes the fd returned from audit_open()
@@ -78,19 +78,36 @@
  * @return
  *  This function returns 0 on success, else -errno.
  */
-extern int  audit_get_reply(int fd, struct audit_message *rep, reply_t block,
-               int peek);
+extern int audit_get_reply(int fd, struct audit_message *rep, reply_t block,
+                           int peek);
 
 /**
- * Sets a pid to recieve audit netlink events from the kernel
+ * Sets a pid to receive audit netlink events from the kernel
  * @param fd
  *  The fd returned by a call to audit_open()
  * @param pid
- *  The pid whom to set as the reciever of audit messages
+ *  The pid whom to set as the receiver of audit messages
  * @return
  *  This function returns 0 on success, -errno on error.
  */
-extern int  audit_setup(int fd, uint32_t pid);
+extern int audit_setup(int fd, pid_t pid);
+
+/**
+ * Sets the rate limit to receive audit netlink events from the kernel
+ * @param fd
+ *  The fd returned by a call to audit_open()
+ * @param max_rate
+ *  The cap of the maximum number of audit messages a second
+ * @return
+ *  This function returns 0 on success, -errno on error.
+ */
+
+/* Guidelines to follow for dynamic rate_limit */
+#define AUDIT_RATE_LIMIT_DEFAULT 20        /* acceptable burst rate      */
+#define AUDIT_RATE_LIMIT_BURST_DURATION 10 /* number of seconds of burst */
+#define AUDIT_RATE_LIMIT_MAX     5         /* acceptable sustained rate  */
+
+extern int audit_rate_limit(int fd, unsigned rate_limit);
 
 __END_DECLS
 
diff --git a/logd/logd.rc b/logd/logd.rc
index 54349dd..ee89b83 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -14,3 +14,10 @@
     user logd
     group logd
     writepid /dev/cpuset/system-background/tasks
+
+on fs
+    write /dev/event-log-tags "# content owned by logd
+"
+    chown logd logd /dev/event-log-tags
+    chmod 0644 /dev/event-log-tags
+    restorecon /dev/event-log-tags
diff --git a/logd/logtagd.rc b/logd/logtagd.rc
new file mode 100644
index 0000000..46aa8c1
--- /dev/null
+++ b/logd/logtagd.rc
@@ -0,0 +1,9 @@
+#
+# logtagd event log tag service (debug only)
+#
+on post-fs-data
+    mkdir /data/misc/logd 0700 logd log
+    write /data/misc/logd/event-log-tags ""
+    chown logd log /data/misc/logd/event-log-tags
+    chmod 0600 /data/misc/logd/event-log-tags
+    restorecon /data/misc/logd/event-log-tags
diff --git a/logd/main.cpp b/logd/main.cpp
index c3343d7..2551f2e 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -244,7 +244,7 @@
     // 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.
+    (void)setuid(AID_LOGD);   // access to everything logd, eg /data/misc/logd
 
     while (reinit_running && !sem_wait(&reinit) && reinit_running) {
 
@@ -271,6 +271,7 @@
             logBuf->init();
             logBuf->initPrune(NULL);
         }
+        android::ReReadEventLogTags();
     }
 
     return NULL;
@@ -304,24 +305,6 @@
     sem_post(&reinit);
 }
 
-// tagToName converts an events tag into a name
-const char *android::tagToName(size_t *len, uint32_t tag) {
-    static const EventTagMap *map;
-
-    if (!map) {
-        sem_wait(&sem_name);
-        if (!map) {
-            map = android_openEventTagMap(NULL);
-        }
-        sem_post(&sem_name);
-        if (!map) {
-            if (len) len = 0;
-            return NULL;
-        }
-    }
-    return android_lookupEventTag_len(map, len, tag);
-}
-
 static void readDmesg(LogAudit *al, LogKlog *kl) {
     if (!al && !kl) {
         return;
@@ -451,9 +434,8 @@
         pthread_attr_destroy(&attr);
     }
 
-    bool auditd = __android_logger_property_get_bool("logd.auditd",
-                                                     BOOL_DEFAULT_TRUE |
-                                                     BOOL_DEFAULT_FLAG_PERSIST);
+    bool auditd = __android_logger_property_get_bool("ro.logd.auditd",
+                                                     BOOL_DEFAULT_TRUE);
     if (drop_privs(klogd, auditd) != 0) {
         return -1;
     }
@@ -513,8 +495,8 @@
     if (auditd) {
         al = new LogAudit(logBuf, reader,
                           __android_logger_property_get_bool(
-                              "logd.auditd.dmesg",
-                              BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST)
+                              "ro.logd.auditd.dmesg",
+                              BOOL_DEFAULT_TRUE)
                                   ? fdDmesg
                                   : -1);
     }
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
index 808087a..c053993 100644
--- a/logd/tests/Android.mk
+++ b/logd/tests/Android.mk
@@ -27,12 +27,15 @@
 # Unit tests.
 # -----------------------------------------------------------------------------
 
+event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004
+
 test_c_flags := \
     -fstack-protector-all \
     -g \
     -Wall -Wextra \
     -Werror \
     -fno-builtin \
+    $(event_flag)
 
 test_src_files := \
     logd_test.cpp
@@ -43,6 +46,6 @@
 LOCAL_MODULE := $(test_module_prefix)unit-tests
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
 LOCAL_SRC_FILES := $(test_src_files)
 include $(BUILD_NATIVE_TEST)
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 254a3f8..8a35059 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -20,6 +20,9 @@
 #include <signal.h>
 #include <stdio.h>
 #include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
 #include <string>
 
@@ -28,15 +31,16 @@
 #include <cutils/sockets.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
+#include <private/android_filesystem_config.h>
+#ifdef __ANDROID__
+#include <selinux/selinux.h>
+#endif
 
+#include "../libaudit.h" // pickup AUDIT_RATE_LIMIT_*
 #include "../LogReader.h" // pickup LOGD_SNDTIMEO
 
-/*
- * returns statistics
- */
-static void my_android_logger_get_statistics(char *buf, size_t len)
+static void send_to_control(char* buf, size_t len)
 {
-    snprintf(buf, len, "getStatistics 0 1 2 3 4");
     int sock = socket_local_client("logd",
                                    ANDROID_SOCKET_NAMESPACE_RESERVED,
                                    SOCK_STREAM);
@@ -44,7 +48,7 @@
         if (write(sock, buf, strlen(buf) + 1) > 0) {
             ssize_t ret;
             while ((ret = read(sock, buf, len)) > 0) {
-                if ((size_t)ret == len) {
+                if (((size_t)ret == len) || (len < PAGE_SIZE)) {
                     break;
                 }
                 len -= ret;
@@ -66,6 +70,15 @@
     }
 }
 
+/*
+ * returns statistics
+ */
+static void my_android_logger_get_statistics(char *buf, size_t len)
+{
+    snprintf(buf, len, "getStatistics 0 1 2 3 4");
+    send_to_control(buf, len);
+}
+
 static void alloc_statistics(char **buffer, size_t *length)
 {
     size_t len = 8192;
@@ -188,7 +201,9 @@
     EXPECT_TRUE(NULL != main_logs);
 
     char *radio_logs = strstr(cp, "\nChattiest UIDs in radio ");
-    EXPECT_TRUE(NULL != radio_logs);
+    if (!radio_logs) GTEST_LOG_(INFO) << "Value of: NULL != radio_logs\n"
+                                         "Actual: false\n"
+                                         "Expected: false\n";
 
     char *system_logs = strstr(cp, "\nChattiest UIDs in system ");
     EXPECT_TRUE(NULL != system_logs);
@@ -415,7 +430,13 @@
 
     // Introduce some extreme spam for the worst UID filter
     ASSERT_TRUE(NULL != (fp = popen(
-        "/data/nativetest/liblog-benchmarks/liblog-benchmarks",
+        "/data/nativetest/liblog-benchmarks/liblog-benchmarks"
+            " BM_log_maximum_retry"
+            " BM_log_maximum"
+            " BM_clock_overhead"
+            " BM_log_overhead"
+            " BM_log_latency"
+            " BM_log_delay",
         "r")));
 
     char buffer[5120];
@@ -581,10 +602,12 @@
             continue;
         }
 
+        // alarm triggers at 50% of the --wrap time out
         content_wrap = recv(fd, msg_wrap.buf, sizeof(msg_wrap), 0) > 0;
 
         alarm_wrap = alarm(5);
 
+        // alarm triggers at 133% of the --wrap time out
         content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
         if (!content_timeout) { // make sure we hit dumpAndClose
             content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
@@ -629,11 +652,24 @@
 
 // b/26447386 refined behavior
 TEST(logd, timeout) {
+    // 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.
+
+    pid_t pid = fork();
+
+    if (pid) {
+        siginfo_t info = {};
+        ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED)));
+        ASSERT_EQ(0, info.si_status);
+        return;
+    }
+
     log_msg msg_wrap, msg_timeout;
     bool content_wrap = false, content_timeout = false, written = false;
     unsigned int alarm_wrap = 0, alarm_timeout = 0;
     // A few tries to get it right just in case wrap kicks in due to
-    // content providers being active during the test
+    // content providers being active during the test.
     int i = 5;
     log_time now(android_log_clockid());
     now.tv_sec -= 30; // reach back a moderate period of time
@@ -642,7 +678,8 @@
         int fd = socket_local_client("logdr",
                                      ANDROID_SOCKET_NAMESPACE_RESERVED,
                                      SOCK_SEQPACKET);
-        ASSERT_LT(0, fd);
+        EXPECT_LT(0, fd);
+        if (fd < 0) _exit(fd);
 
         std::string ask = android::base::StringPrintf(
             "dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=%"
@@ -665,10 +702,12 @@
             continue;
         }
 
+        // alarm triggers at 50% of the --wrap time out
         content_wrap = recv(fd, msg_wrap.buf, sizeof(msg_wrap), 0) > 0;
 
         alarm_wrap = alarm(5);
 
+        // alarm triggers at 133% of the --wrap time out
         content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
         if (!content_timeout) { // make sure we hit dumpAndClose
             content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
@@ -692,6 +731,7 @@
         if (content_timeout) {
             log_time msg(msg_timeout.entry.sec, msg_timeout.entry.nsec);
             EXPECT_FALSE(msg < now);
+            if (msg < now) _exit(-1);
             if (msg > now) {
                 now = msg;
                 now.tv_sec += 30;
@@ -724,6 +764,8 @@
     EXPECT_EQ(0U, alarm_wrap);
     EXPECT_TRUE(content_timeout);
     EXPECT_NE(0U, alarm_timeout);
+
+    _exit(!written + content_wrap + alarm_wrap + !content_timeout + !alarm_timeout);
 }
 
 // b/27242723 confirmed fixed
@@ -778,3 +820,324 @@
 
     close(fd);
 }
+
+TEST(logd, getEventTag_list) {
+#ifdef __ANDROID__
+    char buffer[256];
+    memset(buffer, 0, sizeof(buffer));
+    snprintf(buffer, sizeof(buffer), "getEventTag name=*");
+    send_to_control(buffer, sizeof(buffer));
+    buffer[sizeof(buffer) - 1] = '\0';
+    char *cp;
+    long ret = strtol(buffer, &cp, 10);
+    EXPECT_GT(ret, 4096);
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(logd, getEventTag_42) {
+#ifdef __ANDROID__
+    char buffer[256];
+    memset(buffer, 0, sizeof(buffer));
+    snprintf(buffer, sizeof(buffer), "getEventTag id=42");
+    send_to_control(buffer, sizeof(buffer));
+    buffer[sizeof(buffer) - 1] = '\0';
+    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);
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(logd, getEventTag_newentry) {
+#ifdef __ANDROID__
+    char buffer[256];
+    memset(buffer, 0, sizeof(buffer));
+    log_time now(CLOCK_MONOTONIC);
+    char name[64];
+    snprintf(name, sizeof(name), "a%" PRIu64, now.nsec());
+    snprintf(buffer, sizeof(buffer),
+             "getEventTag name=%s format=\"(new|1)\"", name);
+    send_to_control(buffer, sizeof(buffer));
+    buffer[sizeof(buffer) - 1] = '\0';
+    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);
+    // ToDo: also look for this in /data/misc/logd/event-log-tags and
+    // /dev/event-log-tags.
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+static inline int32_t get4LE(const char* src)
+{
+    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+void __android_log_btwrite_multiple__helper(int count) {
+    log_time ts(CLOCK_MONOTONIC);
+
+    log_time ts1(CLOCK_MONOTONIC);
+
+    // We fork to create a unique pid for the submitted log messages
+    // so that we do not collide with the other _multiple_ tests.
+
+    pid_t pid = fork();
+
+    if (pid == 0) {
+        // child
+        for (int i = count; i; --i) {
+            ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+            usleep(100);
+        }
+        ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
+        usleep(1000000);
+
+        _exit(0);
+    }
+
+    siginfo_t info = {};
+    ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED)));
+    ASSERT_EQ(0, info.si_status);
+
+    struct logger_list *logger_list;
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, pid)));
+
+    int expected_count = (count < 2) ? count : 2;
+    int expected_chatty_count = (count <= 2) ? 0 : 1;
+    int expected_identical_count = (count < 2) ? 0 : (count - 2);
+    static const int expected_expire_count = 0;
+
+    count = 0;
+    int second_count = 0;
+    int chatty_count = 0;
+    int identical_count = 0;
+    int expire_count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
+
+        if ((log_msg.entry.pid != pid) ||
+            (log_msg.entry.len < (4 + 1 + 8)) ||
+            (log_msg.id() != LOG_ID_EVENTS)) continue;
+
+        char *eventData = log_msg.msg();
+        if (!eventData) continue;
+
+        uint32_t tag = get4LE(eventData);
+
+        if ((eventData[4] == EVENT_TYPE_LONG) && (log_msg.entry.len == (4 + 1 + 8))) {
+            if (tag != 0) continue;
+
+            log_time tx(eventData + 4 + 1);
+            if (ts == tx) {
+                ++count;
+            } else if (ts1 == tx) {
+                ++second_count;
+            }
+        } else if (eventData[4] == EVENT_TYPE_STRING) {
+            if (tag != CHATTY_LOG_TAG) continue;
+            ++chatty_count;
+            // int len = get4LE(eventData + 4 + 1);
+            log_msg.buf[LOGGER_ENTRY_MAX_LEN] = '\0';
+            const char *cp;
+            if ((cp = strstr(eventData + 4 + 1 + 4, " identical "))) {
+                unsigned val = 0;
+                sscanf(cp, " identical %u lines", &val);
+                identical_count += val;
+            } else if ((cp = strstr(eventData + 4 + 1 + 4, " expire "))) {
+                unsigned val = 0;
+                sscanf(cp, " expire %u lines", &val);
+                expire_count += val;
+            }
+        }
+    }
+
+    android_logger_list_close(logger_list);
+
+    EXPECT_EQ(expected_count, count);
+    EXPECT_EQ(1, second_count);
+    EXPECT_EQ(expected_chatty_count, chatty_count);
+    EXPECT_EQ(expected_identical_count, identical_count);
+    EXPECT_EQ(expected_expire_count, expire_count);
+}
+
+TEST(logd, multiple_test_1) {
+    __android_log_btwrite_multiple__helper(1);
+}
+
+TEST(logd, multiple_test_2) {
+    __android_log_btwrite_multiple__helper(2);
+}
+
+TEST(logd, multiple_test_3) {
+    __android_log_btwrite_multiple__helper(3);
+}
+
+TEST(logd, multiple_test_10) {
+    __android_log_btwrite_multiple__helper(10);
+}
+
+#ifdef __ANDROID__
+// returns violating pid
+static pid_t sepolicy_rate(unsigned rate, unsigned num) {
+    pid_t pid = fork();
+
+    if (pid) {
+        siginfo_t info = {};
+        if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) return 0;
+        if (info.si_status) return 0;
+        return pid;
+    }
+
+    // We may have DAC, but let's not have MAC
+    if (setcon("u:object_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;
+    }
+
+    // The key here is we are root, but we are in u:r:shell:s0,
+    // and the directory does not provide us DAC access
+    // (eg: 0700 system system) so we trigger the pair dac_override
+    // and dac_read_search on every try to get past the message
+    // de-duper.  We will also rotate the file name in the directory
+    // as another measure.
+    static const char file[] = "/data/backup/cannot_access_directory_%u";
+    static const unsigned avc_requests_per_access = 2;
+
+    rate /= avc_requests_per_access;
+    useconds_t usec;
+    if (rate == 0) {
+        rate = 1;
+        usec = 2000000;
+    } else {
+        usec = (1000000 + (rate / 2)) / rate;
+    }
+    num = (num + (avc_requests_per_access / 2)) / avc_requests_per_access;
+
+    if (usec < 2) usec = 2;
+
+    while (num > 0) {
+        if (access(android::base::StringPrintf(file, num).c_str(), F_OK) == 0) {
+            _exit(-1);
+            // NOTREACHED
+            return 0;
+        }
+        usleep(usec);
+        --num;
+    }
+    _exit(0);
+    // NOTREACHED
+    return 0;
+}
+
+static int count_avc(pid_t pid) {
+    int count = 0;
+
+    if (pid == 0) 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))) return count;
+    for (;;) {
+        log_msg log_msg;
+
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
+
+        if ((log_msg.entry.pid != pid) ||
+            (log_msg.entry.len < (4 + 1 + 8)) ||
+            (log_msg.id() != LOG_ID_EVENTS)) continue;
+
+        char *eventData = log_msg.msg();
+        if (!eventData) continue;
+
+        uint32_t tag = get4LE(eventData);
+        if (tag != AUDITD_LOG_TAG) continue;
+
+        if (eventData[4] != EVENT_TYPE_STRING) continue;
+
+        // int len = get4LE(eventData + 4 + 1);
+        log_msg.buf[LOGGER_ENTRY_MAX_LEN] = '\0';
+        const char *cp = strstr(eventData + 4 + 1 + 4, "): avc: denied");
+        if (!cp) continue;
+
+        ++count;
+    }
+
+    android_logger_list_close(logger_list);
+
+    return count;
+}
+#endif
+
+TEST(logd, sepolicy_rate_limiter_maximum) {
+#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)));
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c
index 1bfecd6..3ad0983 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.c
@@ -28,8 +28,8 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include <android/log.h>
 #include <cutils/klog.h>
+#include <log/log.h>
 #include <logwrap/logwrap.h>
 #include <private/android_filesystem_config.h>
 
diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.c
index 28fe530..33454c6 100644
--- a/logwrapper/logwrapper.c
+++ b/logwrapper/logwrapper.c
@@ -20,8 +20,8 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include <android/log.h>
 #include <cutils/klog.h>
+#include <log/log.h>
 #include <logwrap/logwrap.h>
 
 void fatal(const char *msg) {
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 7d0c87d..03f878a 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -116,6 +116,12 @@
   EXPORT_GLOBAL_ASAN_OPTIONS := export ASAN_OPTIONS include=/system/asan.options
   LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES)
 endif
+
+EXPORT_GLOBAL_GCOV_OPTIONS :=
+ifeq ($(NATIVE_COVERAGE),true)
+  EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/gcov
+endif
+
 # Put it here instead of in init.rc module definition,
 # because init.rc is conditionally included.
 #
@@ -163,6 +169,7 @@
 	$(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
 	$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
+	$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
 
 bcp_md5 :=
 bcp_dep :=
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index e6c94ff..ec541b9 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -1,4 +1,5 @@
 libandroid.so
+libaaudio.so
 libc.so
 libcamera2ndk.so
 libdl.so
@@ -12,6 +13,7 @@
 liblog.so
 libmediandk.so
 libm.so
+libnativewindow.so
 libOpenMAXAL.so
 libOpenSLES.so
 libRS.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 292730a..d8b5350 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -1,4 +1,5 @@
 libandroid.so
+libaaudio.so
 libc.so
 libcamera2ndk.so
 libdl.so
@@ -12,6 +13,7 @@
 liblog.so
 libmediandk.so
 libm.so
+libnativewindow.so
 libOpenMAXAL.so
 libOpenSLES.so
 libRS.so
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 32817fa..2e2ab74 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -10,3 +10,4 @@
     export BOOTCLASSPATH %BOOTCLASSPATH%
     export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
     %EXPORT_GLOBAL_ASAN_OPTIONS%
+    %EXPORT_GLOBAL_GCOV_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 11f91ce..c6546b9 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -118,13 +118,18 @@
     write /proc/sys/kernel/sched_child_runs_first 0
 
     write /proc/sys/kernel/randomize_va_space 2
-    write /proc/sys/kernel/kptr_restrict 2
     write /proc/sys/vm/mmap_min_addr 32768
     write /proc/sys/net/ipv4/ping_group_range "0 2147483647"
     write /proc/sys/net/unix/max_dgram_qlen 600
     write /proc/sys/kernel/sched_rt_runtime_us 950000
     write /proc/sys/kernel/sched_rt_period_us 1000000
 
+    # Assign reasonable ceiling values for socket rcv/snd buffers.
+    # These should almost always be overridden by the target per the
+    # the corresponding technology maximums.
+    write /proc/sys/net/core/rmem_max  262144
+    write /proc/sys/net/core/wmem_max  262144
+
     # reflect fwmark from incoming packets onto generated replies
     write /proc/sys/net/ipv4/fwmark_reflect 1
     write /proc/sys/net/ipv6/fwmark_reflect 1
@@ -307,6 +312,7 @@
     # Make sure /sys/kernel/debug (if present) is labeled properly
     # Note that tracefs may be mounted under debug, so we need to cross filesystems
     restorecon --recursive --cross-filesystems /sys/kernel/debug
+    chmod 0755 /sys/kernel/debug/tracing
 
     # We chown/chmod /cache again so because mount is run as root + defaults
     chown system cache /cache
@@ -350,10 +356,6 @@
     # We restorecon /data in case the userdata partition has been reset.
     restorecon /data
 
-    # start debuggerd to make debugging early-boot crashes easier.
-    start debuggerd
-    start debuggerd64
-
     # Make sure we have the device encryption key.
     start vold
     installkey /data
@@ -361,7 +363,7 @@
     # Start bootcharting as soon as possible after the data partition is
     # mounted to collect more data.
     mkdir /data/bootchart 0755 shell shell
-    bootchart_init
+    bootchart start
 
     # Avoid predictable entropy pool. Carry over entropy from previous boot.
     copy /data/system/entropy.dat /dev/urandom
@@ -401,11 +403,13 @@
     mkdir /data/misc/boottrace 0771 system shell
     mkdir /data/misc/update_engine 0700 root root
     mkdir /data/misc/trace 0700 root root
+    mkdir /data/misc/reboot 0700 root root
     # profile file layout
     mkdir /data/misc/profiles 0771 system system
     mkdir /data/misc/profiles/cur 0771 system system
     mkdir /data/misc/profiles/ref 0771 system system
     mkdir /data/misc/profman 0770 system shell
+    mkdir /data/misc/gcov 0770 root root
 
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp
@@ -481,7 +485,7 @@
     # Check any timezone data in /data is newer than the copy in /system, delete if not.
     exec - system system -- /system/bin/tzdatacheck /system/usr/share/zoneinfo /data/misc/zoneinfo
 
-    # If there is no fs-post-data action in the init.<device>.rc file, you
+    # If there is no post-fs-data action in the init.<device>.rc file, you
     # must uncomment this line, otherwise encrypted filesystems
     # won't work.
     # Set indication (checked by vold) that we have finished this action
@@ -619,6 +623,9 @@
 on property:sys.powerctl=*
     powerctl ${sys.powerctl}
 
+on property:sys.boot_completed=1
+    bootchart stop
+
 # system server cannot write to /proc/sys files,
 # and chown/chmod does not work for /proc/sys/ entries.
 # So proxy writes through init.
diff --git a/rootdir/init.usb.configfs.rc b/rootdir/init.usb.configfs.rc
index dc875b4..32f0198 100644
--- a/rootdir/init.usb.configfs.rc
+++ b/rootdir/init.usb.configfs.rc
@@ -70,7 +70,7 @@
 
 on property:sys.usb.config=audio_source && property:sys.usb.configfs=1
     write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "audiosource"
-    symlink /config/usb_gadget/g1/functions/audio_source.gs2 /config/usb_gadget/g1/configs/b.1/f1
+    symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f1
     write /config/usb_gadget/g1/UDC ${sys.usb.controller}
     setprop sys.usb.state ${sys.usb.config}
 
@@ -79,7 +79,7 @@
 
 on property:sys.usb.ffs.ready=1 && property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=1
     write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "audiosource_adb"
-    symlink /config/usb_gadget/g1/functions/audio_source.gs2 /config/usb_gadget/g1/configs/b.1/f1
+    symlink /config/usb_gadget/g1/functions/audio_source.gs3 /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}
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index eedeba8..d836c4e 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -10,4 +10,5 @@
     onrestart restart cameraserver
     onrestart restart media
     onrestart restart netd
+    onrestart restart wificond
     writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 84a907f..80bb673 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -10,7 +10,8 @@
     onrestart restart cameraserver
     onrestart restart media
     onrestart restart netd
-    writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+    onrestart restart wificond
+    writepid /dev/cpuset/foreground/tasks
 
 service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
     class main
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 76e2b79..05ec16f 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -10,4 +10,5 @@
     onrestart restart cameraserver
     onrestart restart media
     onrestart restart netd
+    onrestart restart wificond
     writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index e918b67..09db7b0 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -10,9 +10,10 @@
     onrestart restart cameraserver
     onrestart restart media
     onrestart restart netd
-    writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+    onrestart restart wificond
+    writepid /dev/cpuset/foreground/tasks
 
-service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
+service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
     class main
     priority -20
     user root
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 8725113..0633a68 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -38,8 +38,6 @@
 # these should not be world writable
 /dev/diag                 0660   radio      radio
 /dev/diag_arm9            0660   radio      radio
-/dev/android_adb          0660   adb        adb
-/dev/android_adb_enable   0660   adb        adb
 /dev/ttyMSM0              0600   bluetooth  bluetooth
 /dev/uhid                 0660   system     bluetooth
 /dev/uinput               0660   system     bluetooth
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index aec51f4..e7b8cc2 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -165,12 +165,12 @@
   if (setegid(old_egid) == -1) error(1, errno, "couldn't restore egid");
 
   // Verify that user id is not too big.
-  if ((UID_MAX - info.uid) / AID_USER < (uid_t)userId) {
+  if ((UID_MAX - info.uid) / AID_USER_OFFSET < (uid_t)userId) {
     error(1, 0, "user id too big: %d", userId);
   }
 
   // Calculate user app ID.
-  uid_t userAppId = (AID_USER * userId) + info.uid;
+  uid_t userAppId = (AID_USER_OFFSET * userId) + info.uid;
 
   // Reject system packages.
   if (userAppId < AID_APP) {
diff --git a/storaged/Android.mk b/storaged/Android.mk
new file mode 100644
index 0000000..2adb14d
--- /dev/null
+++ b/storaged/Android.mk
@@ -0,0 +1,44 @@
+# Copyright 2016 The Android Open Source Project
+
+LOCAL_PATH := $(call my-dir)
+
+LIBSTORAGED_SHARED_LIBRARIES := \
+    libbinder \
+    libbase \
+    libutils \
+    libcutils \
+    liblog \
+    libsysutils \
+    libpackagelistparser \
+    libbatteryservice \
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    storaged.cpp \
+    storaged_service.cpp \
+    storaged_utils.cpp \
+    storaged_uid_monitor.cpp \
+    EventLogTags.logtags
+
+LOCAL_MODULE := libstoraged
+LOCAL_CFLAGS := -Werror
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include external/googletest/googletest/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := $(LIBSTORAGED_SHARED_LIBRARIES)
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := storaged
+LOCAL_INIT_RC := storaged.rc
+LOCAL_SRC_FILES := main.cpp
+# libstoraged is an internal static library, only main.cpp and storaged_test.cpp should be using it
+LOCAL_STATIC_LIBRARIES := libstoraged
+LOCAL_SHARED_LIBRARIES := $(LIBSTORAGED_SHARED_LIBRARIES)
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
+LOCAL_C_INCLUDES := external/googletest/googletest/include
+
+include $(BUILD_EXECUTABLE)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/storaged/EventLogTags.logtags b/storaged/EventLogTags.logtags
new file mode 100644
index 0000000..2e25d4a
--- /dev/null
+++ b/storaged/EventLogTags.logtags
@@ -0,0 +1,39 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31.  (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_".  Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace.  Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+#    (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+# 5: float
+#
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# Default value for data of type int/long is 2 (bytes).
+#
+# TODO: generate ".java" and ".h" files with integer constants from this file.
+
+2732 storaged_disk_stats (type|3),(start_time|2|3),(end_time|2|3),(read_ios|2|1),(read_merges|2|1),(read_sectors|2|1),(read_ticks|2|3),(write_ios|2|1),(write_merges|2|1),(write_sectors|2|1),(write_ticks|2|3),(o_in_flight|2|1),(io_ticks|2|3),(io_in_queue|2|1)
+
+2733 storaged_emmc_info (mmc_ver|3),(eol|1),(lifetime_a|1),(lifetime_b|1)
\ No newline at end of file
diff --git a/storaged/README.properties b/storaged/README.properties
new file mode 100644
index 0000000..70e6026
--- /dev/null
+++ b/storaged/README.properties
@@ -0,0 +1,6 @@
+ro.storaged.event.interval    # interval storaged scans for IO stats, in seconds
+ro.storaged.event.perf_check  # check for time spent in event loop, in microseconds
+ro.storaged.disk_stats_pub    # interval storaged publish disk stats, in seconds
+ro.storaged.emmc_info_pub     # interval storaged publish emmc info, in seconds
+ro.storaged.uid_io.interval   # interval storaged checks Per UID IO usage, in seconds
+ro.storaged.uid_io.threshold  # Per UID IO usage limit, in bytes
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
new file mode 100644
index 0000000..c291bd9
--- /dev/null
+++ b/storaged/include/storaged.h
@@ -0,0 +1,324 @@
+/*
+ * 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 _STORAGED_H_
+#define _STORAGED_H_
+
+#include <semaphore.h>
+#include <stdint.h>
+#include <time.h>
+
+#include <queue>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <batteryservice/IBatteryPropertiesListener.h>
+
+#include "storaged_uid_monitor.h"
+
+using namespace android;
+
+#define FRIEND_TEST(test_case_name, test_name) \
+friend class test_case_name##_##test_name##_Test
+
+/* For debug */
+#ifdef DEBUG
+#define debuginfo(fmt, ...) \
+ do {printf("%s():\t" fmt "\t[%s:%d]\n", __FUNCTION__, ##__VA_ARGS__, __FILE__, __LINE__);} \
+ while(0)
+#else
+#define debuginfo(...)
+#endif
+
+#define SECTOR_SIZE ( 512 )
+#define SEC_TO_MSEC ( 1000 )
+#define MSEC_TO_USEC ( 1000 )
+#define USEC_TO_NSEC ( 1000 )
+#define SEC_TO_USEC ( 1000000 )
+#define HOUR_TO_SEC ( 3600 )
+#define DAY_TO_SEC ( 3600 * 24 )
+
+// number of attributes diskstats has
+#define DISK_STATS_SIZE ( 11 )
+// maximum size limit of a stats file
+#define DISK_STATS_FILE_MAX_SIZE ( 256 )
+#define DISK_STATS_IO_IN_FLIGHT_IDX ( 8 )
+struct disk_stats {
+    /* It will be extremely unlikely for any of the following entries to overflow.
+     * For read_bytes(which will be greater than any of the following entries), it
+     * will take 27 years to overflow uint64_t at the reading rate of 20GB/s, which
+     * is the peak memory transfer rate for current memory.
+     * The diskstats entries (first 11) need to be at top in this structure _after_
+     * compiler's optimization.
+     */
+    uint64_t read_ios;       // number of read I/Os processed
+    uint64_t read_merges;    // number of read I/Os merged with in-queue I/Os
+    uint64_t read_sectors;   // number of sectors read
+    uint64_t read_ticks;     // total wait time for read requests
+    uint64_t write_ios;      // number of write I/Os processed
+    uint64_t write_merges;   // number of write I/Os merged with in-queue I/Os
+    uint64_t write_sectors;  // number of sectors written
+    uint64_t write_ticks;    // total wait time for write requests
+    uint64_t io_in_flight;   // number of I/Os currently in flight
+    uint64_t io_ticks;       // total time this block device has been active
+    uint64_t io_in_queue;    // total wait time for all requests
+
+    uint64_t start_time;     // monotonic time accounting starts
+    uint64_t end_time;       // monotonic time accounting ends
+    uint32_t counter;        // private counter for accumulate calculations
+    double   io_avg;         // average io_in_flight for accumulate calculations
+};
+
+#define MMC_VER_STR_LEN ( 9 )   // maximum length of the MMC version string, including NULL terminator
+// minimum size of a ext_csd file
+#define EXT_CSD_FILE_MIN_SIZE ( 1024 )
+struct emmc_info {
+    int eol;                        // pre-eol (end of life) information
+    int lifetime_a;                 // device life time estimation (type A)
+    int lifetime_b;                 // device life time estimation (type B)
+    char mmc_ver[MMC_VER_STR_LEN];  // device version string
+};
+
+struct disk_perf {
+    uint32_t read_perf;         // read speed (kbytes/s)
+    uint32_t read_ios;          // read I/Os per second
+    uint32_t write_perf;        // write speed (kbytes/s)
+    uint32_t write_ios;         // write I/Os per second
+    uint32_t queue;             // I/Os in queue
+};
+
+#define CMD_MAX_LEN ( 64 )
+struct task_info {
+    uint32_t pid;                   // task id
+    uint64_t rchar;                 // characters read
+    uint64_t wchar;                 // characters written
+    uint64_t syscr;                 // read syscalls
+    uint64_t syscw;                 // write syscalls
+    uint64_t read_bytes;            // bytes read (from storage layer)
+    uint64_t write_bytes;           // bytes written (to storage layer)
+    uint64_t cancelled_write_bytes; // cancelled write byte by truncate
+
+    uint64_t starttime;             // start time of task
+
+    char cmd[CMD_MAX_LEN];          // filename of the executable
+};
+
+class lock_t {
+    sem_t* mSem;
+public:
+    lock_t(sem_t* sem) {
+        mSem = sem;
+        sem_wait(mSem);
+    }
+    ~lock_t() {
+        sem_post(mSem);
+    }
+};
+
+class stream_stats {
+private:
+    double mSum;
+    double mSquareSum;
+    uint32_t mCnt;
+public:
+    stream_stats() : mSum(0), mSquareSum(0), mCnt(0) {};
+    ~stream_stats() {};
+    double get_mean() {
+        return mSum / mCnt;
+    }
+    double get_std() {
+        return sqrt(mSquareSum / mCnt - mSum * mSum / (mCnt * mCnt));
+    }
+    void add(uint32_t num) {
+        mSum += (double)num;
+        mSquareSum += (double)num * (double)num;
+        mCnt++;
+    }
+    void evict(uint32_t num) {
+        if (mSum < num || mSquareSum < (double)num * (double)num) return;
+        mSum -= (double)num;
+        mSquareSum -= (double)num * (double)num;
+        mCnt--;
+    }
+};
+
+#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
+#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
+#define EMMC_ECSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
+#define UID_IO_STATS_PATH "/proc/uid_io/stats"
+
+class disk_stats_monitor {
+private:
+    FRIEND_TEST(storaged_test, disk_stats_monitor);
+    const char* DISK_STATS_PATH;
+    struct disk_stats mPrevious;
+    struct disk_stats mAccumulate;
+    bool mStall;
+    std::queue<struct disk_perf> mBuffer;
+    struct {
+        stream_stats read_perf;           // read speed (bytes/s)
+        stream_stats read_ios;            // read I/Os per second
+        stream_stats write_perf;          // write speed (bytes/s)
+        stream_stats write_ios;           // write I/O per second
+        stream_stats queue;               // I/Os in queue
+    } mStats;
+    bool mValid;
+    const uint32_t mWindow;
+    const double mSigma;
+    struct disk_perf mMean;
+    struct disk_perf mStd;
+
+    void update_mean();
+    void update_std();
+    void add(struct disk_perf* perf);
+    void evict(struct disk_perf* perf);
+    bool detect(struct disk_perf* perf);
+
+    void update(struct disk_stats* stats);
+
+public:
+    disk_stats_monitor(uint32_t window_size = 5, double sigma = 1.0) :
+            mStall(false),
+            mValid(false),
+            mWindow(window_size),
+            mSigma(sigma) {
+        memset(&mPrevious, 0, sizeof(mPrevious));
+        memset(&mMean, 0, sizeof(mMean));
+        memset(&mStd, 0, sizeof(mStd));
+
+        if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
+            DISK_STATS_PATH = MMC_DISK_STATS_PATH;
+        } else {
+            DISK_STATS_PATH = SDA_DISK_STATS_PATH;
+        }
+    }
+    void update(void);
+};
+
+class disk_stats_publisher {
+private:
+    FRIEND_TEST(storaged_test, disk_stats_publisher);
+    const char* DISK_STATS_PATH;
+    struct disk_stats mAccumulate;
+    struct disk_stats mPrevious;
+public:
+    disk_stats_publisher(void) {
+        memset(&mAccumulate, 0, sizeof(struct disk_stats));
+        memset(&mPrevious, 0, sizeof(struct disk_stats));
+
+        if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
+            DISK_STATS_PATH = MMC_DISK_STATS_PATH;
+        } else {
+            DISK_STATS_PATH = SDA_DISK_STATS_PATH;
+        }
+    }
+
+    ~disk_stats_publisher(void) {}
+    void publish(void);
+    void update(void);
+};
+
+class emmc_info_t {
+private:
+    struct emmc_info mInfo;
+    bool mValid;
+    int mFdEmmc;
+public:
+    emmc_info_t(void) :
+            mValid(false),
+            mFdEmmc(-1) {
+        memset(&mInfo, 0, sizeof(struct emmc_info));
+    }
+    ~emmc_info_t(void) {}
+
+    void publish(void);
+    void update(void);
+    void set_emmc_fd(int fd) {
+        mFdEmmc = fd;
+    }
+};
+
+// Periodic chores intervals in seconds
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH ( 86400 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO ( 3600 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT (300)
+
+// UID IO threshold in bytes
+#define DEFAULT_PERIODIC_CHORES_UID_IO_THRESHOLD ( 1024 * 1024 * 1024ULL )
+
+struct storaged_config {
+    int periodic_chores_interval_unit;
+    int periodic_chores_interval_disk_stats_publish;
+    int periodic_chores_interval_emmc_info_publish;
+    int periodic_chores_interval_uid_io;
+    bool proc_uid_io_available;      // whether uid_io is accessible
+    bool emmc_available;        // whether eMMC est_csd file is readable
+    bool diskstats_available;   // whether diskstats is accessible
+    int event_time_check_usec;  // check how much cputime spent in event loop
+};
+
+class storaged_t : public BnBatteryPropertiesListener {
+private:
+    time_t mTimer;
+    storaged_config mConfig;
+    disk_stats_publisher mDiskStats;
+    disk_stats_monitor mDsm;
+    emmc_info_t mEmmcInfo;
+    uid_monitor mUidm;
+    time_t mStarttime;
+public:
+    storaged_t(void);
+    ~storaged_t() {}
+    void event(void);
+    void event_checked(void);
+    void pause(void) {
+        sleep(mConfig.periodic_chores_interval_unit);
+    }
+    void set_privileged_fds(int fd_emmc) {
+        mEmmcInfo.set_emmc_fd(fd_emmc);
+    }
+
+    time_t get_starttime(void) {
+        return mStarttime;
+    }
+
+    std::unordered_map<uint32_t, struct uid_info> get_uids(void) {
+        return mUidm.get_uid_io_stats();
+    }
+    std::map<uint64_t, struct uid_records> get_uid_records(
+            double hours, uint64_t threshold, bool force_report) {
+        return mUidm.dump(hours, threshold, force_report);
+    }
+    void update_uid_io_interval(int interval) {
+        if (interval >= DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT) {
+            mConfig.periodic_chores_interval_uid_io = interval;
+        }
+    }
+
+    void init_battery_service();
+    virtual void batteryPropertiesChanged(struct BatteryProperties props);
+};
+
+// Eventlog tag
+// The content must match the definition in EventLogTags.logtags
+#define EVENTLOGTAG_DISKSTATS ( 2732 )
+#define EVENTLOGTAG_EMMCINFO ( 2733 )
+#define EVENTLOGTAG_UID_IO_ALERT ( 2734 )
+
+#endif /* _STORAGED_H_ */
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
new file mode 100644
index 0000000..a8ddf4c
--- /dev/null
+++ b/storaged/include/storaged_service.h
@@ -0,0 +1,60 @@
+/*
+ * 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 _STORAGED_SERVICE_H_
+#define _STORAGED_SERVICE_H_
+
+#include <vector>
+
+#include <binder/IInterface.h>
+#include <binder/IBinder.h>
+
+#include "storaged.h"
+
+using namespace android;
+
+// Interface
+class IStoraged : public IInterface {
+public:
+    enum {
+        DUMPUIDS  = IBinder::FIRST_CALL_TRANSACTION,
+    };
+    // Request the service to run the test function
+    virtual std::vector<struct uid_info> dump_uids(const char* option) = 0;
+
+    DECLARE_META_INTERFACE(Storaged);
+};
+
+// Client
+class BpStoraged : public BpInterface<IStoraged> {
+public:
+    BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){};
+    virtual std::vector<struct uid_info> dump_uids(const char* option);
+};
+
+// Server
+class BnStoraged : public BnInterface<IStoraged> {
+    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
+};
+
+class Storaged : public BnStoraged {
+    virtual std::vector<struct uid_info> dump_uids(const char* option);
+    virtual status_t dump(int fd, const Vector<String16>& args);
+};
+
+sp<IStoraged> get_storaged_service();
+
+#endif /* _STORAGED_SERVICE_H_ */
\ No newline at end of file
diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h
new file mode 100644
index 0000000..901a872
--- /dev/null
+++ b/storaged/include/storaged_uid_monitor.h
@@ -0,0 +1,110 @@
+/*
+ * 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 _STORAGED_UID_MONITOR_H_
+#define _STORAGED_UID_MONITOR_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+enum uid_stat_t {
+    FOREGROUND = 0,
+    BACKGROUND = 1,
+    UID_STATS = 2
+};
+
+enum charger_stat_t {
+    CHARGER_OFF = 0,
+    CHARGER_ON = 1,
+    CHARGER_STATS = 2
+};
+
+enum io_type_t {
+    READ = 0,
+    WRITE = 1,
+    IO_TYPES = 2
+};
+
+struct uid_io_stats {
+    uint64_t rchar;                 // characters read
+    uint64_t wchar;                 // characters written
+    uint64_t read_bytes;            // bytes read (from storage layer)
+    uint64_t write_bytes;           // bytes written (to storage layer)
+    uint64_t fsync;                 // number of fsync syscalls
+};
+
+struct uid_info {
+    uint32_t uid;                   // user id
+    std::string name;               // package name
+    struct uid_io_stats io[UID_STATS];    // [0]:foreground [1]:background
+};
+
+struct uid_io_usage {
+    uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS];
+};
+
+struct uid_record {
+    std::string name;
+    struct uid_io_usage ios;
+};
+
+struct uid_records {
+    uint64_t start_ts;
+    std::vector<struct uid_record> entries;
+};
+
+class uid_monitor {
+private:
+    // last dump from /proc/uid_io/stats, uid -> uid_info
+    std::unordered_map<uint32_t, struct uid_info> last_uid_io_stats;
+    // current io usage for next report, app name -> uid_io_usage
+    std::unordered_map<std::string, struct uid_io_usage> curr_io_stats;
+    // io usage records, end timestamp -> {start timestamp, vector of records}
+    std::map<uint64_t, struct uid_records> records;
+    // charger ON/OFF
+    charger_stat_t charger_stat;
+    // protects curr_io_stats, last_uid_io_stats, records and charger_stat
+    sem_t um_lock;
+    // start time for IO records
+    uint64_t start_ts;
+
+    // reads from /proc/uid_io/stats
+    std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats_locked();
+    // flushes curr_io_stats to records
+    void add_records_locked(uint64_t curr_ts);
+    // updates curr_io_stats and set last_uid_io_stats
+    void update_curr_io_stats_locked();
+
+public:
+    uid_monitor();
+    ~uid_monitor();
+    // called by storaged main thread
+    void init(charger_stat_t stat);
+    // called by storaged -u
+    std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats();
+    // called by dumpsys
+    std::map<uint64_t, struct uid_records> dump(
+        double hours, uint64_t threshold, bool force_report);
+    // called by battery properties listener
+    void set_charger_state(charger_stat_t stat);
+    // called by storaged periodic_chore or dump with force_report
+    void report();
+};
+
+#endif /* _STORAGED_UID_MONITOR_H_ */
diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h
new file mode 100644
index 0000000..2161c40
--- /dev/null
+++ b/storaged/include/storaged_utils.h
@@ -0,0 +1,44 @@
+/*
+ * 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 _STORAGED_UTILS_H_
+#define _STORAGED_UTILS_H_
+
+#include <stdint.h>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "storaged.h"
+
+// Diskstats
+bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats);
+struct disk_perf get_disk_perf(struct disk_stats* stats);
+struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr);
+void add_disk_stats(struct disk_stats* src, struct disk_stats* dst);
+bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info);
+
+// UID I/O
+void sort_running_uids_info(std::vector<struct uid_info> &uids);
+
+// Logging
+void log_console_running_uids_info(std::vector<struct uid_info> uids);
+
+void log_debug_disk_perf(struct disk_perf* perf, const char* type);
+
+void log_event_disk_stats(struct disk_stats* stats, const char* type);
+void log_event_emmc_info(struct emmc_info* info_);
+#endif /* _STORAGED_UTILS_H_ */
diff --git a/storaged/main.cpp b/storaged/main.cpp
new file mode 100644
index 0000000..672f453
--- /dev/null
+++ b/storaged/main.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "storaged"
+#define KLOG_LEVEL 6
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <cutils/android_get_control_file.h>
+#include <cutils/sched_policy.h>
+#include <private/android_filesystem_config.h>
+
+#include <storaged.h>
+#include <storaged_service.h>
+#include <storaged_utils.h>
+
+storaged_t storaged;
+
+// Function of storaged's main thread
+void* storaged_main(void* s) {
+    storaged_t* storaged = (storaged_t*)s;
+
+    storaged->init_battery_service();
+
+    LOG_TO(SYSTEM, INFO) << "storaged: Start";
+
+    for (;;) {
+        storaged->event_checked();
+        storaged->pause();
+    }
+    return NULL;
+}
+
+static void help_message(void) {
+    printf("usage: storaged [OPTION]\n");
+    printf("  -u    --uid                   Dump uid I/O usage to stdout\n");
+    printf("  -s    --start                 Start storaged (default)\n");
+    fflush(stdout);
+}
+
+int main(int argc, char** argv) {
+    int flag_main_service = 0;
+    int flag_dump_uid = 0;
+    int fd_emmc = -1;
+    int opt;
+
+    for (;;) {
+        int opt_idx = 0;
+        static struct option long_options[] = {
+            {"start",       no_argument,        0, 's'},
+            {"kill",        no_argument,        0, 'k'},
+            {"uid",         no_argument,        0, 'u'},
+            {"help",        no_argument,        0, 'h'}
+        };
+        opt = getopt_long(argc, argv, ":skdhu0", long_options, &opt_idx);
+        if (opt == -1) {
+            break;
+        }
+
+        switch (opt) {
+        case 's':
+            flag_main_service = 1;
+            break;
+        case 'u':
+            flag_dump_uid = 1;
+            break;
+        case 'h':
+            help_message();
+            return 0;
+        case '?':
+        default:
+            fprintf(stderr, "no supported option\n");
+            help_message();
+            return -1;
+        }
+    }
+
+    if (argc == 1) {
+        flag_main_service = 1;
+    }
+
+    if (flag_main_service && flag_dump_uid) {
+        fprintf(stderr, "Invalid arguments. Option \"start\" and \"dump\" cannot be used together.\n");
+        help_message();
+        return -1;
+    }
+
+    if (flag_main_service) { // start main thread
+        static const char mmc0_ext_csd[] = "/d/mmc0/mmc0:0001/ext_csd";
+        fd_emmc = android_get_control_file(mmc0_ext_csd);
+        if (fd_emmc < 0)
+            fd_emmc = TEMP_FAILURE_RETRY(open(mmc0_ext_csd, O_RDONLY));
+
+        storaged.set_privileged_fds(fd_emmc);
+
+        // Start the main thread of storaged
+        pthread_t storaged_main_thread;
+        errno = pthread_create(&storaged_main_thread, NULL, storaged_main, &storaged);
+        if (errno != 0) {
+            PLOG_TO(SYSTEM, ERROR) << "Failed to create main thread";
+            return -1;
+        }
+
+        defaultServiceManager()->addService(String16("storaged"), new Storaged());
+        android::ProcessState::self()->startThreadPool();
+        IPCThreadState::self()->joinThreadPool();
+        pthread_join(storaged_main_thread, NULL);
+
+        close(fd_emmc);
+
+        return 0;
+    }
+
+    if (flag_dump_uid) {
+        sp<IStoraged> storaged_service = get_storaged_service();
+        if (storaged_service == NULL) {
+            fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
+            return -1;
+        }
+        std::vector<struct uid_info> res = storaged_service->dump_uids(NULL);
+
+        if (res.size() == 0) {
+            fprintf(stderr, "UID I/O is not readable in this version of kernel.\n");
+            return 0;
+        }
+
+        sort_running_uids_info(res);
+        log_console_running_uids_info(res);
+
+        return 0;
+    }
+
+    return 0;
+}
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
new file mode 100644
index 0000000..2f02074
--- /dev/null
+++ b/storaged/storaged.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "storaged"
+
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <batteryservice/BatteryServiceConstants.h>
+#include <batteryservice/IBatteryPropertiesRegistrar.h>
+#include <binder/IServiceManager.h>
+#include <cutils/properties.h>
+#include <log/log.h>
+
+#include <storaged.h>
+#include <storaged_utils.h>
+
+/* disk_stats_publisher */
+void disk_stats_publisher::publish(void) {
+    // Logging
+    struct disk_perf perf = get_disk_perf(&mAccumulate);
+    log_debug_disk_perf(&perf, "regular");
+    log_event_disk_stats(&mAccumulate, "regular");
+    // Reset global structures
+    memset(&mAccumulate, 0, sizeof(struct disk_stats));
+}
+
+void disk_stats_publisher::update(void) {
+    struct disk_stats curr;
+    if (parse_disk_stats(DISK_STATS_PATH, &curr)) {
+        struct disk_stats inc = get_inc_disk_stats(&mPrevious, &curr);
+        add_disk_stats(&inc, &mAccumulate);
+#ifdef DEBUG
+//            log_kernel_disk_stats(&mPrevious, "prev stats");
+//            log_kernel_disk_stats(&curr, "curr stats");
+//            log_kernel_disk_stats(&inc, "inc stats");
+//            log_kernel_disk_stats(&mAccumulate, "accumulated stats");
+#endif
+        mPrevious = curr;
+    }
+}
+
+/* disk_stats_monitor */
+void disk_stats_monitor::update_mean() {
+    CHECK(mValid);
+    mMean.read_perf = (uint32_t)mStats.read_perf.get_mean();
+    mMean.read_ios = (uint32_t)mStats.read_ios.get_mean();
+    mMean.write_perf = (uint32_t)mStats.write_perf.get_mean();
+    mMean.write_ios = (uint32_t)mStats.write_ios.get_mean();
+    mMean.queue = (uint32_t)mStats.queue.get_mean();
+}
+
+void disk_stats_monitor::update_std() {
+    CHECK(mValid);
+    mStd.read_perf = (uint32_t)mStats.read_perf.get_std();
+    mStd.read_ios = (uint32_t)mStats.read_ios.get_std();
+    mStd.write_perf = (uint32_t)mStats.write_perf.get_std();
+    mStd.write_ios = (uint32_t)mStats.write_ios.get_std();
+    mStd.queue = (uint32_t)mStats.queue.get_std();
+}
+
+void disk_stats_monitor::add(struct disk_perf* perf) {
+    mStats.read_perf.add(perf->read_perf);
+    mStats.read_ios.add(perf->read_ios);
+    mStats.write_perf.add(perf->write_perf);
+    mStats.write_ios.add(perf->write_ios);
+    mStats.queue.add(perf->queue);
+}
+
+void disk_stats_monitor::evict(struct disk_perf* perf) {
+    mStats.read_perf.evict(perf->read_perf);
+    mStats.read_ios.evict(perf->read_ios);
+    mStats.write_perf.evict(perf->write_perf);
+    mStats.write_ios.evict(perf->write_ios);
+    mStats.queue.evict(perf->queue);
+}
+
+bool disk_stats_monitor::detect(struct disk_perf* perf) {
+    return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) &&
+            ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) &&
+            ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf);
+}
+
+void disk_stats_monitor::update(struct disk_stats* stats) {
+    struct disk_stats inc = get_inc_disk_stats(&mPrevious, stats);
+    struct disk_perf perf = get_disk_perf(&inc);
+    // Update internal data structures
+    if (LIKELY(mValid)) {
+        CHECK_EQ(mBuffer.size(), mWindow);
+
+        if (UNLIKELY(detect(&perf))) {
+            mStall = true;
+            add_disk_stats(&inc, &mAccumulate);
+            log_debug_disk_perf(&mMean, "stalled_mean");
+            log_debug_disk_perf(&mStd, "stalled_std");
+        } else {
+            if (mStall) {
+                struct disk_perf acc_perf = get_disk_perf(&mAccumulate);
+                log_debug_disk_perf(&acc_perf, "stalled");
+                log_event_disk_stats(&mAccumulate, "stalled");
+                mStall = false;
+                memset(&mAccumulate, 0, sizeof(mAccumulate));
+            }
+        }
+
+        evict(&mBuffer.front());
+        mBuffer.pop();
+        add(&perf);
+        mBuffer.push(perf);
+
+        update_mean();
+        update_std();
+
+    } else { /* mValid == false */
+        CHECK_LT(mBuffer.size(), mWindow);
+        add(&perf);
+        mBuffer.push(perf);
+        if (mBuffer.size() == mWindow) {
+            mValid = true;
+            update_mean();
+            update_std();
+        }
+    }
+
+    mPrevious = *stats;
+}
+
+void disk_stats_monitor::update(void) {
+    struct disk_stats curr;
+    if (LIKELY(parse_disk_stats(DISK_STATS_PATH, &curr))) {
+        update(&curr);
+    }
+}
+
+/* emmc_info_t */
+void emmc_info_t::publish(void) {
+    if (mValid) {
+        log_event_emmc_info(&mInfo);
+    }
+}
+
+void emmc_info_t::update(void) {
+    if (mFdEmmc >= 0) {
+        mValid = parse_emmc_ecsd(mFdEmmc, &mInfo);
+    }
+}
+
+static sp<IBatteryPropertiesRegistrar> get_battery_properties_service() {
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == NULL) return NULL;
+
+    sp<IBinder> binder = sm->getService(String16("batteryproperties"));
+    if (binder == NULL) return NULL;
+
+    sp<IBatteryPropertiesRegistrar> battery_properties =
+        interface_cast<IBatteryPropertiesRegistrar>(binder);
+
+    return battery_properties;
+}
+
+static inline charger_stat_t is_charger_on(int64_t prop) {
+    return (prop == BATTERY_STATUS_CHARGING || prop == BATTERY_STATUS_FULL) ?
+        CHARGER_ON : CHARGER_OFF;
+}
+
+void storaged_t::batteryPropertiesChanged(struct BatteryProperties props) {
+    mUidm.set_charger_state(is_charger_on(props.batteryStatus));
+}
+
+void storaged_t::init_battery_service() {
+    sp<IBatteryPropertiesRegistrar> battery_properties = get_battery_properties_service();
+    if (battery_properties == NULL) {
+        LOG_TO(SYSTEM, WARNING) << "failed to find batteryproperties service";
+        return;
+    }
+
+    struct BatteryProperty val;
+    battery_properties->getProperty(BATTERY_PROP_BATTERY_STATUS, &val);
+    mUidm.init(is_charger_on(val.valueInt64));
+
+    // register listener after init uid_monitor
+    battery_properties->registerListener(this);
+}
+
+/* storaged_t */
+storaged_t::storaged_t(void) {
+    mConfig.emmc_available = (access(EMMC_ECSD_PATH, R_OK) >= 0);
+
+    if (access(MMC_DISK_STATS_PATH, R_OK) < 0 && access(SDA_DISK_STATS_PATH, R_OK) < 0) {
+        mConfig.diskstats_available = false;
+    } else {
+        mConfig.diskstats_available = true;
+    }
+
+    mConfig.proc_uid_io_available = (access(UID_IO_STATS_PATH, R_OK) == 0);
+
+    mConfig.periodic_chores_interval_unit =
+        property_get_int32("ro.storaged.event.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT);
+
+    mConfig.event_time_check_usec =
+        property_get_int32("ro.storaged.event.perf_check", 0);
+
+    mConfig.periodic_chores_interval_disk_stats_publish =
+        property_get_int32("ro.storaged.disk_stats_pub", DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH);
+
+    mConfig.periodic_chores_interval_emmc_info_publish =
+        property_get_int32("ro.storaged.emmc_info_pub", DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH);
+
+    mConfig.periodic_chores_interval_uid_io =
+        property_get_int32("ro.storaged.uid_io.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);
+
+    mStarttime = time(NULL);
+}
+
+void storaged_t::event(void) {
+    if (mConfig.diskstats_available) {
+        mDiskStats.update();
+        mDsm.update();
+        if (mTimer && (mTimer % mConfig.periodic_chores_interval_disk_stats_publish) == 0) {
+            mDiskStats.publish();
+        }
+    }
+
+    if (mConfig.emmc_available && mTimer &&
+            (mTimer % mConfig.periodic_chores_interval_emmc_info_publish) == 0) {
+        mEmmcInfo.update();
+        mEmmcInfo.publish();
+    }
+
+    if (mConfig.proc_uid_io_available && mTimer &&
+            (mTimer % mConfig.periodic_chores_interval_uid_io) == 0) {
+         mUidm.report();
+    }
+
+    mTimer += mConfig.periodic_chores_interval_unit;
+}
+
+void storaged_t::event_checked(void) {
+    struct timespec start_ts, end_ts;
+    bool check_time = true;
+
+    if (mConfig.event_time_check_usec &&
+        clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_ts) < 0) {
+        check_time = false;
+        static time_t state_a;
+        IF_ALOG_RATELIMIT_LOCAL(300, &state_a) {
+            PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+        }
+    }
+
+    event();
+
+    if (mConfig.event_time_check_usec && check_time) {
+        if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_ts) < 0) {
+            static time_t state_b;
+            IF_ALOG_RATELIMIT_LOCAL(300, &state_b) {
+                PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+            }
+            return;
+        }
+        int64_t cost = (end_ts.tv_sec - start_ts.tv_sec) * SEC_TO_USEC +
+                       (end_ts.tv_nsec - start_ts.tv_nsec) / USEC_TO_NSEC;
+        if (cost > mConfig.event_time_check_usec) {
+            LOG_TO(SYSTEM, ERROR)
+                << "event loop spent " << cost << " usec, threshold "
+                << mConfig.event_time_check_usec << " usec";
+        }
+    }
+}
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
new file mode 100644
index 0000000..a24c7fb
--- /dev/null
+++ b/storaged/storaged.rc
@@ -0,0 +1,7 @@
+service storaged /system/bin/storaged
+    class main
+    priority 10
+    file /d/mmc0/mmc0:0001/ext_csd r
+    writepid /dev/cpuset/system-background/tasks
+    user root
+    group package_info
\ No newline at end of file
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
new file mode 100644
index 0000000..a335298
--- /dev/null
+++ b/storaged/storaged_service.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/PermissionCache.h>
+#include <private/android_filesystem_config.h>
+
+#include <storaged.h>
+#include <storaged_service.h>
+
+extern storaged_t storaged;
+
+std::vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) {
+    Parcel data, reply;
+    data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
+
+    remote()->transact(DUMPUIDS, data, &reply);
+
+    uint32_t res_size = reply.readInt32();
+    std::vector<struct uid_info> res(res_size);
+    for (auto&& uid : res) {
+        uid.uid = reply.readInt32();
+        uid.name = reply.readCString();
+        reply.read(&uid.io, sizeof(uid.io));
+    }
+    return res;
+}
+IMPLEMENT_META_INTERFACE(Storaged, "Storaged");
+
+status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+    switch(code) {
+        case DUMPUIDS: {
+                if (!data.checkInterface(this))
+                    return BAD_TYPE;
+                std::vector<struct uid_info> res = dump_uids(NULL);
+                reply->writeInt32(res.size());
+                for (auto uid : res) {
+                    reply->writeInt32(uid.uid);
+                    reply->writeCString(uid.name.c_str());
+                    reply->write(&uid.io, sizeof(uid.io));
+                }
+                return NO_ERROR;
+            }
+            break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+std::vector<struct uid_info> Storaged::dump_uids(const char* /* option */) {
+    std::vector<struct uid_info> uids_v;
+    std::unordered_map<uint32_t, struct uid_info> uids_m = storaged.get_uids();
+
+    for (const auto& it : uids_m) {
+        uids_v.push_back(it.second);
+    }
+    return uids_v;
+}
+
+status_t Storaged::dump(int fd, const Vector<String16>& args) {
+    IPCThreadState* self = IPCThreadState::self();
+    const int pid = self->getCallingPid();
+    const int uid = self->getCallingUid();
+    if ((uid != AID_SHELL) &&
+        !PermissionCache::checkPermission(
+                String16("android.permission.DUMP"), pid, uid)) {
+        return PERMISSION_DENIED;
+    }
+
+    double hours = 0;
+    int time_window = 0;
+    uint64_t threshold = 0;
+    bool force_report = false;
+    for (size_t i = 0; i < args.size(); i++) {
+        const auto& arg = args[i];
+        if (arg == String16("--hours")) {
+            if (++i >= args.size())
+                break;
+            hours = stod(String16::std_string(args[i]));
+            continue;
+        }
+        if (arg == String16("--time_window")) {
+            if (++i >= args.size())
+                break;
+            time_window = stoi(String16::std_string(args[i]));
+            continue;
+        }
+        if (arg == String16("--threshold")) {
+            if (++i >= args.size())
+                break;
+            threshold = stoll(String16::std_string(args[i]));
+            continue;
+        }
+        if (arg == String16("--force")) {
+            force_report = true;
+            continue;
+        }
+    }
+
+    uint64_t last_ts = 0;
+    const std::map<uint64_t, struct uid_records>& records =
+                storaged.get_uid_records(hours, threshold, force_report);
+    for (const auto& it : records) {
+        if (last_ts != it.second.start_ts) {
+            dprintf(fd, "%llu", (unsigned long long)it.second.start_ts);
+        }
+        dprintf(fd, ",%llu\n", (unsigned long long)it.first);
+        last_ts = it.first;
+
+        for (const auto& record : it.second.entries) {
+            dprintf(fd, "%s %llu %llu %llu %llu %llu %llu %llu %llu\n",
+                record.name.c_str(),
+                (unsigned long long)record.ios.bytes[READ][FOREGROUND][CHARGER_OFF],
+                (unsigned long long)record.ios.bytes[WRITE][FOREGROUND][CHARGER_OFF],
+                (unsigned long long)record.ios.bytes[READ][BACKGROUND][CHARGER_OFF],
+                (unsigned long long)record.ios.bytes[WRITE][BACKGROUND][CHARGER_OFF],
+                (unsigned long long)record.ios.bytes[READ][FOREGROUND][CHARGER_ON],
+                (unsigned long long)record.ios.bytes[WRITE][FOREGROUND][CHARGER_ON],
+                (unsigned long long)record.ios.bytes[READ][BACKGROUND][CHARGER_ON],
+                (unsigned long long)record.ios.bytes[WRITE][BACKGROUND][CHARGER_ON]);
+        }
+    }
+
+    if (time_window) {
+        storaged.update_uid_io_interval(time_window);
+    }
+
+    return NO_ERROR;
+}
+
+sp<IStoraged> get_storaged_service() {
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == NULL) return NULL;
+
+    sp<IBinder> binder = sm->getService(String16("storaged"));
+    if (binder == NULL) return NULL;
+
+    sp<IStoraged> storaged = interface_cast<IStoraged>(binder);
+
+    return storaged;
+}
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
new file mode 100644
index 0000000..8bb6bf3
--- /dev/null
+++ b/storaged/storaged_uid_monitor.cpp
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "storaged"
+
+#include <stdint.h>
+#include <time.h>
+
+#include <string>
+#include <unordered_map>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+#include <android-base/stringprintf.h>
+#include <log/log_event_list.h>
+#include <packagelistparser/packagelistparser.h>
+
+#include "storaged.h"
+#include "storaged_uid_monitor.h"
+
+using namespace android;
+using namespace android::base;
+
+static bool packagelist_parse_cb(pkg_info* info, void* userdata)
+{
+    std::unordered_map<uint32_t, struct uid_info>* uids =
+        reinterpret_cast<std::unordered_map<uint32_t, struct uid_info>*>(userdata);
+
+    if (uids->find(info->uid) != uids->end()) {
+        (*uids)[info->uid].name = info->name;
+    }
+
+    packagelist_free(info);
+    return true;
+}
+
+std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats()
+{
+    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+    return get_uid_io_stats_locked();
+};
+
+std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats_locked()
+{
+    std::unordered_map<uint32_t, struct uid_info> uid_io_stats;
+    std::string buffer;
+    if (!android::base::ReadFileToString(UID_IO_STATS_PATH, &buffer)) {
+        PLOG_TO(SYSTEM, ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
+        return uid_io_stats;
+    }
+
+    std::vector<std::string> io_stats = android::base::Split(buffer, "\n");
+    struct uid_info u;
+    bool refresh_uid = false;
+
+    for (uint32_t i = 0; i < io_stats.size(); i++) {
+        if (io_stats[i].empty()) {
+            continue;
+        }
+        std::vector<std::string> fields = android::base::Split(io_stats[i], " ");
+        if (fields.size() < 9) {
+            LOG_TO(SYSTEM, WARNING) << "Invalid io stats: \""
+                                    << io_stats[i] << "\"";
+            continue;
+        }
+        u.uid = stoul(fields[0]);
+        u.io[FOREGROUND].rchar = stoull(fields[1]);
+        u.io[FOREGROUND].wchar = stoull(fields[2]);
+        u.io[FOREGROUND].read_bytes = stoull(fields[3]);
+        u.io[FOREGROUND].write_bytes = stoull(fields[4]);
+        u.io[BACKGROUND].rchar = stoull(fields[5]);
+        u.io[BACKGROUND].wchar = stoull(fields[6]);
+        u.io[BACKGROUND].read_bytes = stoull(fields[7]);
+        u.io[BACKGROUND].write_bytes = stoull(fields[8]);
+
+        if (fields.size() == 11) {
+            u.io[FOREGROUND].fsync = stoull(fields[9]);
+            u.io[BACKGROUND].fsync = stoull(fields[10]);
+        }
+
+        if (last_uid_io_stats.find(u.uid) == last_uid_io_stats.end()) {
+            refresh_uid = true;
+            u.name = std::to_string(u.uid);
+        } else {
+            u.name = last_uid_io_stats[u.uid].name;
+        }
+        uid_io_stats[u.uid] = u;
+    }
+
+    if (refresh_uid) {
+        packagelist_parse(packagelist_parse_cb, &uid_io_stats);
+    }
+
+    return uid_io_stats;
+}
+
+static const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
+
+static inline int records_size(
+    const std::map<uint64_t, struct uid_records>& curr_records)
+{
+    int count = 0;
+    for (auto const& it : curr_records) {
+        count += it.second.entries.size();
+    }
+    return count;
+}
+
+static struct uid_io_usage zero_io_usage;
+
+void uid_monitor::add_records_locked(uint64_t curr_ts)
+{
+    // remove records more than 5 days old
+    if (curr_ts > 5 * DAY_TO_SEC) {
+        auto it = records.lower_bound(curr_ts - 5 * DAY_TO_SEC);
+        records.erase(records.begin(), it);
+    }
+
+    struct uid_records new_records;
+    for (const auto& p : curr_io_stats) {
+        struct uid_record record = {};
+        record.name = p.first;
+        record.ios = p.second;
+        if (memcmp(&record.ios, &zero_io_usage, sizeof(struct uid_io_usage))) {
+            new_records.entries.push_back(record);
+        }
+    }
+
+    curr_io_stats.clear();
+    new_records.start_ts = start_ts;
+    start_ts = curr_ts;
+
+    if (new_records.entries.empty())
+      return;
+
+    // make some room for new records
+    int overflow = records_size(records) +
+        new_records.entries.size() - MAX_UID_RECORDS_SIZE;
+    while (overflow > 0 && records.size() > 0) {
+        auto del_it = records.begin();
+        overflow -= del_it->second.entries.size();
+        records.erase(records.begin());
+    }
+
+    records[curr_ts] = new_records;
+}
+
+std::map<uint64_t, struct uid_records> uid_monitor::dump(
+    double hours, uint64_t threshold, bool force_report)
+{
+    if (force_report) {
+        report();
+    }
+
+    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+
+    std::map<uint64_t, struct uid_records> dump_records;
+    uint64_t first_ts = 0;
+
+    if (hours != 0) {
+        first_ts = time(NULL) - hours * HOUR_TO_SEC;
+    }
+
+    for (auto it = records.lower_bound(first_ts); it != records.end(); ++it) {
+        const std::vector<struct uid_record>& recs = it->second.entries;
+        struct uid_records filtered;
+
+        for (const auto& rec : recs) {
+            if (rec.ios.bytes[READ][FOREGROUND][CHARGER_ON] +
+                rec.ios.bytes[READ][FOREGROUND][CHARGER_OFF] +
+                rec.ios.bytes[READ][BACKGROUND][CHARGER_ON] +
+                rec.ios.bytes[READ][BACKGROUND][CHARGER_OFF] +
+                rec.ios.bytes[WRITE][FOREGROUND][CHARGER_ON] +
+                rec.ios.bytes[WRITE][FOREGROUND][CHARGER_OFF] +
+                rec.ios.bytes[WRITE][BACKGROUND][CHARGER_ON] +
+                rec.ios.bytes[WRITE][BACKGROUND][CHARGER_OFF] > threshold) {
+                filtered.entries.push_back(rec);
+            }
+        }
+
+        if (filtered.entries.empty())
+            continue;
+
+        filtered.start_ts = it->second.start_ts;
+        dump_records.insert(
+            std::pair<uint64_t, struct uid_records>(it->first, filtered));
+    }
+
+    return dump_records;
+}
+
+void uid_monitor::update_curr_io_stats_locked()
+{
+    std::unordered_map<uint32_t, struct uid_info> uid_io_stats =
+        get_uid_io_stats_locked();
+    if (uid_io_stats.empty()) {
+        return;
+    }
+
+    for (const auto& it : uid_io_stats) {
+        const struct uid_info& uid = it.second;
+
+        if (curr_io_stats.find(uid.name) == curr_io_stats.end()) {
+          curr_io_stats[uid.name] = {};
+        }
+
+        struct uid_io_usage& usage = curr_io_stats[uid.name];
+        int64_t fg_rd_delta = uid.io[FOREGROUND].read_bytes -
+            last_uid_io_stats[uid.uid].io[FOREGROUND].read_bytes;
+        int64_t bg_rd_delta = uid.io[BACKGROUND].read_bytes -
+            last_uid_io_stats[uid.uid].io[BACKGROUND].read_bytes;
+        int64_t fg_wr_delta = uid.io[FOREGROUND].write_bytes -
+            last_uid_io_stats[uid.uid].io[FOREGROUND].write_bytes;
+        int64_t bg_wr_delta = uid.io[BACKGROUND].write_bytes -
+            last_uid_io_stats[uid.uid].io[BACKGROUND].write_bytes;
+
+        usage.bytes[READ][FOREGROUND][charger_stat] +=
+            (fg_rd_delta < 0) ? uid.io[FOREGROUND].read_bytes : fg_rd_delta;
+        usage.bytes[READ][BACKGROUND][charger_stat] +=
+            (bg_rd_delta < 0) ? uid.io[BACKGROUND].read_bytes : bg_rd_delta;
+        usage.bytes[WRITE][FOREGROUND][charger_stat] +=
+            (fg_wr_delta < 0) ? uid.io[FOREGROUND].write_bytes : fg_wr_delta;
+        usage.bytes[WRITE][BACKGROUND][charger_stat] +=
+            (bg_wr_delta < 0) ? uid.io[BACKGROUND].write_bytes : bg_wr_delta;
+    }
+
+    last_uid_io_stats = uid_io_stats;
+}
+
+void uid_monitor::report()
+{
+    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+
+    update_curr_io_stats_locked();
+    add_records_locked(time(NULL));
+}
+
+void uid_monitor::set_charger_state(charger_stat_t stat)
+{
+    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+
+    if (charger_stat == stat) {
+        return;
+    }
+
+    update_curr_io_stats_locked();
+    charger_stat = stat;
+}
+
+void uid_monitor::init(charger_stat_t stat)
+{
+    charger_stat = stat;
+    start_ts = time(NULL);
+    last_uid_io_stats = get_uid_io_stats();
+}
+
+uid_monitor::uid_monitor()
+{
+    sem_init(&um_lock, 0, 1);
+}
+
+uid_monitor::~uid_monitor()
+{
+    sem_destroy(&um_lock);
+}
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
new file mode 100644
index 0000000..5df0185
--- /dev/null
+++ b/storaged/storaged_utils.cpp
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "storaged"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/time.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <log/log_event_list.h>
+
+#include <storaged.h>
+#include <storaged_utils.h>
+
+bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) {
+    // Get time
+    struct timespec ts;
+    // Use monotonic to exclude suspend time so that we measure IO bytes/sec
+    // when system is running.
+    int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+    if (ret < 0) {
+        PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+        return false;
+    }
+
+    std::string buffer;
+    if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
+        PLOG_TO(SYSTEM, ERROR) << disk_stats_path << ": ReadFileToString failed.";
+        return false;
+    }
+
+    // Regular diskstats entries
+    std::stringstream ss(buffer);
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        ss >> *((uint64_t*)stats + i);
+    }
+    // Other entries
+    stats->start_time = 0;
+    stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC +
+        ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC);
+    stats->counter = 1;
+    stats->io_avg = (double)stats->io_in_flight;
+    return true;
+}
+
+struct disk_perf get_disk_perf(struct disk_stats* stats) {
+    struct disk_perf perf;
+    memset(&perf, 0, sizeof(struct disk_perf));  // initialize
+
+    if (stats->io_ticks) {
+        if (stats->read_ticks) {
+            unsigned long long divisor = stats->read_ticks * stats->io_ticks;
+            perf.read_perf = ((unsigned long long)SECTOR_SIZE *
+                                        stats->read_sectors *
+                                        stats->io_in_queue +
+                                        (divisor >> 1)) /
+                                            divisor;
+            perf.read_ios = ((unsigned long long)SEC_TO_MSEC *
+                                        stats->read_ios *
+                                        stats->io_in_queue +
+                                        (divisor >> 1)) /
+                                            divisor;
+        }
+        if (stats->write_ticks) {
+            unsigned long long divisor = stats->write_ticks * stats->io_ticks;
+                        perf.write_perf = ((unsigned long long)SECTOR_SIZE *
+                                                    stats->write_sectors *
+                                                    stats->io_in_queue +
+                                                    (divisor >> 1)) /
+                                                        divisor;
+                        perf.write_ios = ((unsigned long long)SEC_TO_MSEC *
+                                                    stats->write_ios *
+                                                    stats->io_in_queue +
+                                                    (divisor >> 1)) /
+                                                        divisor;
+        }
+        perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) /
+                                stats->io_ticks;
+    }
+    return perf;
+}
+
+struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr) {
+    struct disk_stats inc;
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        if (i == DISK_STATS_IO_IN_FLIGHT_IDX) {
+            continue;
+        }
+
+        *((uint64_t*)&inc + i) =
+                *((uint64_t*)curr + i) - *((uint64_t*)prev + i);
+    }
+    // io_in_flight is exception
+    inc.io_in_flight = curr->io_in_flight;
+
+    inc.start_time = prev->end_time;
+    inc.end_time = curr->end_time;
+    inc.io_avg = curr->io_avg;
+    inc.counter = 1;
+
+    return inc;
+}
+
+// Add src to dst
+void add_disk_stats(struct disk_stats* src, struct disk_stats* dst) {
+    if (dst->end_time != 0 && dst->end_time != src->start_time) {
+        LOG_TO(SYSTEM, WARNING) << "Two dis-continuous periods of diskstats"
+            << " are added. dst end with " << dst->end_time
+            << ", src start with " << src->start_time;
+    }
+
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        if (i == DISK_STATS_IO_IN_FLIGHT_IDX) {
+            continue;
+        }
+
+        *((uint64_t*)dst + i) += *((uint64_t*)src + i);
+    }
+
+    dst->io_in_flight = src->io_in_flight;
+    if (dst->counter + src->counter) {
+        dst->io_avg = ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) /
+                        (dst->counter + src->counter);
+    }
+    dst->counter += src->counter;
+    dst->end_time = src->end_time;
+    if (dst->start_time == 0) {
+        dst->start_time = src->start_time;
+    }
+}
+
+bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info) {
+    CHECK(ext_csd_fd >= 0);
+    struct hex {
+        char str[2];
+    };
+    // List of interesting offsets
+    static const size_t EXT_CSD_REV_IDX = 192 * sizeof(hex);
+    static const size_t EXT_PRE_EOL_INFO_IDX = 267 * sizeof(hex);
+    static const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * sizeof(hex);
+    static const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * sizeof(hex);
+
+    // Read file
+    CHECK(lseek(ext_csd_fd, 0, SEEK_SET) == 0);
+    std::string buffer;
+    if (!android::base::ReadFdToString(ext_csd_fd, &buffer)) {
+        PLOG_TO(SYSTEM, ERROR) << "ReadFdToString failed.";
+        return false;
+    }
+
+    if (buffer.length() < EXT_CSD_FILE_MIN_SIZE) {
+        LOG_TO(SYSTEM, ERROR) << "EMMC ext csd file has truncated content. "
+            << "File length: " << buffer.length();
+        return false;
+    }
+
+    std::string sub;
+    std::stringstream ss;
+    // Parse EXT_CSD_REV
+    int ext_csd_rev = -1;
+    sub = buffer.substr(EXT_CSD_REV_IDX, sizeof(hex));
+    ss << sub;
+    ss >> std::hex >> ext_csd_rev;
+    if (ext_csd_rev < 0) {
+        LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_CSD_REV.";
+        return false;
+    }
+    ss.clear();
+
+    static const char* ver_str[] = {
+        "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0"
+    };
+
+    strlcpy(info->mmc_ver,
+            (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ?
+                           ver_str[ext_csd_rev] :
+                           "Unknown",
+            MMC_VER_STR_LEN);
+
+    if (ext_csd_rev < 7) {
+        return 0;
+    }
+
+    // Parse EXT_PRE_EOL_INFO
+    info->eol = -1;
+    sub = buffer.substr(EXT_PRE_EOL_INFO_IDX, sizeof(hex));
+    ss << sub;
+    ss >> std::hex >> info->eol;
+    if (info->eol < 0) {
+        LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_PRE_EOL_INFO.";
+        return false;
+    }
+    ss.clear();
+
+    // Parse DEVICE_LIFE_TIME_EST
+    info->lifetime_a = -1;
+    sub = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, sizeof(hex));
+    ss << sub;
+    ss >> std::hex >> info->lifetime_a;
+    if (info->lifetime_a < 0) {
+        LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_DEVICE_LIFE_TIME_EST_TYP_A.";
+        return false;
+    }
+    ss.clear();
+
+    info->lifetime_b = -1;
+    sub = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, sizeof(hex));
+    ss << sub;
+    ss >> std::hex >> info->lifetime_b;
+    if (info->lifetime_b < 0) {
+        LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_DEVICE_LIFE_TIME_EST_TYP_B.";
+        return false;
+    }
+    ss.clear();
+
+    return true;
+}
+
+static bool cmp_uid_info(struct uid_info l, struct uid_info r) {
+    // Compare background I/O first.
+    for (int i = UID_STATS - 1; i >= 0; i--) {
+        uint64_t l_bytes = l.io[i].read_bytes + l.io[i].write_bytes;
+        uint64_t r_bytes = r.io[i].read_bytes + r.io[i].write_bytes;
+        uint64_t l_chars = l.io[i].rchar + l.io[i].wchar;
+        uint64_t r_chars = r.io[i].rchar + r.io[i].wchar;
+
+        if (l_bytes != r_bytes) {
+            return l_bytes > r_bytes;
+        }
+        if (l_chars != r_chars) {
+            return l_chars > r_chars;
+        }
+    }
+
+    return l.name < r.name;
+}
+
+void sort_running_uids_info(std::vector<struct uid_info> &uids) {
+    std::sort(uids.begin(), uids.end(), cmp_uid_info);
+}
+
+// Logging functions
+void log_console_running_uids_info(std::vector<struct uid_info> uids) {
+// Sample Output:
+//                                       Application        FG Read       FG Write        FG Read       FG Write        BG Read       BG Write        BG Read       BG Write
+//                                          NAME/UID     Characters     Characters          Bytes          Bytes     Characters     Characters          Bytes          Bytes
+//                                        ----------     ----------     ----------     ----------     ----------     ----------     ----------     ----------     ----------
+//                      com.google.android.gsf.login              0              0              0              0       57195097        5137089      176386048        6512640
+//           com.google.android.googlequicksearchbox              0              0              0              0        4196821       12123468       34295808       13225984
+//                                              1037           4572            537              0              0         131352        5145643       34263040        5144576
+//                        com.google.android.youtube           2182             70              0              0       63969383         482939       38731776         466944
+
+    // Title
+    printf("Per-UID I/O stats\n");
+    printf("                                       Application        FG Read       FG Write        FG Read       FG Write        BG Read       BG Write        BG Read       BG Write       FG fsync       BG fsync\n"
+           "                                          NAME/UID     Characters     Characters          Bytes          Bytes     Characters     Characters          Bytes          Bytes                              \n"
+           "                                        ----------     ----------     ----------     ----------     ----------     ----------     ----------     ----------     ----------     ----------     ----------\n");
+
+    for (const auto& uid : uids) {
+        printf("%50s%15ju%15ju%15ju%15ju%15ju%15ju%15ju%15ju%15ju%15ju\n", uid.name.c_str(),
+            uid.io[0].rchar, uid.io[0].wchar, uid.io[0].read_bytes, uid.io[0].write_bytes,
+            uid.io[1].rchar, uid.io[1].wchar, uid.io[1].read_bytes, uid.io[1].write_bytes,
+            uid.io[0].fsync, uid.io[1].fsync);
+    }
+    fflush(stdout);
+}
+
+#if DEBUG
+void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
+    // skip if the input structure are all zeros
+    if (perf == NULL) return;
+    struct disk_perf zero_cmp;
+    memset(&zero_cmp, 0, sizeof(zero_cmp));
+    if (memcmp(&zero_cmp, perf, sizeof(struct disk_perf)) == 0) return;
+
+    LOG_TO(SYSTEM, INFO) << "perf(ios) " << type
+              << " rd:" << perf->read_perf << "KB/s(" << perf->read_ios << "/s)"
+              << " wr:" << perf->write_perf << "KB/s(" << perf->write_ios << "/s)"
+              << " q:" << perf->queue;
+}
+#else
+void log_debug_disk_perf(struct disk_perf* /* perf */, const char* /* type */) {}
+#endif
+
+void log_event_disk_stats(struct disk_stats* stats, const char* type) {
+    // skip if the input structure are all zeros
+    if (stats == NULL) return;
+    struct disk_stats zero_cmp;
+    memset(&zero_cmp, 0, sizeof(zero_cmp));
+    // skip event logging diskstats when it is zero increment (all first 11 entries are zero)
+    if (memcmp(&zero_cmp, stats, sizeof(uint64_t) * DISK_STATS_SIZE) == 0) return;
+
+    android_log_event_list(EVENTLOGTAG_DISKSTATS)
+        << type << stats->start_time << stats->end_time
+        << stats->read_ios << stats->read_merges
+        << stats->read_sectors << stats->read_ticks
+        << stats->write_ios << stats->write_merges
+        << stats->write_sectors << stats->write_ticks
+        << (uint64_t)stats->io_avg << stats->io_ticks << stats->io_in_queue
+        << LOG_ID_EVENTS;
+}
+
+void log_event_emmc_info(struct emmc_info* info) {
+    // skip if the input structure are all zeros
+    if (info == NULL) return;
+    struct emmc_info zero_cmp;
+    memset(&zero_cmp, 0, sizeof(zero_cmp));
+    if (memcmp(&zero_cmp, info, sizeof(struct emmc_info)) == 0) return;
+
+    android_log_event_list(EVENTLOGTAG_EMMCINFO)
+        << info->mmc_ver << info->eol << info->lifetime_a << info->lifetime_b
+        << LOG_ID_EVENTS;
+}
diff --git a/storaged/tests/Android.mk b/storaged/tests/Android.mk
new file mode 100644
index 0000000..26d04b1
--- /dev/null
+++ b/storaged/tests/Android.mk
@@ -0,0 +1,45 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+test_module_prefix := storaged-
+test_tags := tests
+
+# -----------------------------------------------------------------------------
+# Unit tests.
+# -----------------------------------------------------------------------------
+
+test_c_flags := \
+    -fstack-protector-all \
+    -g \
+    -Wall -Wextra \
+    -Werror \
+    -fno-builtin \
+
+test_src_files := \
+    storaged_test.cpp \
+
+# Build tests for the logger. Run with:
+#   adb shell /data/nativetest/storaged-unit-tests/storaged-unit-tests
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(test_module_prefix)unit-tests
+LOCAL_MODULE_TAGS := $(test_tags)
+LOCAL_CFLAGS += $(test_c_flags)
+LOCAL_STATIC_LIBRARIES := libstoraged
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libpackagelistparser
+LOCAL_SRC_FILES := $(test_src_files)
+include $(BUILD_NATIVE_TEST)
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
new file mode 100644
index 0000000..9e03c50
--- /dev/null
+++ b/storaged/tests/storaged_test.cpp
@@ -0,0 +1,373 @@
+/*
+ * 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 <deque>
+#include <fcntl.h>
+#include <random>
+#include <string.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <storaged.h>               // data structures
+#include <storaged_utils.h>         // functions to test
+
+#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
+#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
+#define EMMC_EXT_CSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
+
+static void pause(uint32_t sec) {
+    const char* path = "/cache/test";
+    int fd = open(path, O_WRONLY | O_CREAT, 0600);
+    ASSERT_LT(-1, fd);
+    char buffer[2048];
+    memset(buffer, 1, sizeof(buffer));
+    int loop_size = 100;
+    for (int i = 0; i < loop_size; ++i) {
+        ASSERT_EQ(2048, write(fd, buffer, sizeof(buffer)));
+    }
+    fsync(fd);
+    close(fd);
+
+    fd = open(path, O_RDONLY);
+    ASSERT_LT(-1, fd);
+    for (int i = 0; i < loop_size; ++i) {
+        ASSERT_EQ(2048, read(fd, buffer, sizeof(buffer)));
+    }
+    close(fd);
+
+    sleep(sec);
+}
+
+// the return values of the tested functions should be the expected ones
+const char* DISK_STATS_PATH;
+TEST(storaged_test, retvals) {
+    struct disk_stats stats;
+    struct emmc_info info;
+    memset(&stats, 0, sizeof(struct disk_stats));
+    memset(&info, 0, sizeof(struct emmc_info));
+
+    int emmc_fd = open(EMMC_EXT_CSD_PATH, O_RDONLY);
+    if (emmc_fd >= 0) {
+        EXPECT_TRUE(parse_emmc_ecsd(emmc_fd, &info));
+    }
+
+    if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
+        DISK_STATS_PATH = MMC_DISK_STATS_PATH;
+    } else if (access(SDA_DISK_STATS_PATH, R_OK) >= 0) {
+        DISK_STATS_PATH = SDA_DISK_STATS_PATH;
+    } else {
+        return;
+    }
+
+    EXPECT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
+
+    struct disk_stats old_stats;
+    memset(&old_stats, 0, sizeof(struct disk_stats));
+    old_stats = stats;
+
+    const char wrong_path[] = "/this/is/wrong";
+    EXPECT_FALSE(parse_disk_stats(wrong_path, &stats));
+
+    // reading a wrong path should not damage the output structure
+    EXPECT_EQ(0, memcmp(&stats, &old_stats, sizeof(disk_stats)));
+}
+
+TEST(storaged_test, disk_stats) {
+    struct disk_stats stats;
+    memset(&stats, 0, sizeof(struct disk_stats));
+
+    ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
+
+    // every entry of stats (except io_in_flight) should all be greater than 0
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        if (i == 8) continue; // skip io_in_flight which can be 0
+        EXPECT_LT((uint64_t)0, *((uint64_t*)&stats + i));
+    }
+
+    // accumulation of the increments should be the same with the overall increment
+    struct disk_stats base, tmp, curr, acc, inc[5];
+    memset(&base, 0, sizeof(struct disk_stats));
+    memset(&tmp, 0, sizeof(struct disk_stats));
+    memset(&acc, 0, sizeof(struct disk_stats));
+
+    for (uint i = 0; i < 5; ++i) {
+        ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &curr));
+        if (i == 0) {
+            base = curr;
+            tmp = curr;
+            sleep(5);
+            continue;
+        }
+        inc[i] = get_inc_disk_stats(&tmp, &curr);
+        add_disk_stats(&inc[i], &acc);
+        tmp = curr;
+        pause(5);
+    }
+    struct disk_stats overall_inc;
+    memset(&overall_inc, 0, sizeof(disk_stats));
+    overall_inc= get_inc_disk_stats(&base, &curr);
+
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        if (i == 8) continue; // skip io_in_flight which can be 0
+        EXPECT_EQ(*((uint64_t*)&overall_inc + i), *((uint64_t*)&acc + i));
+    }
+}
+
+TEST(storaged_test, emmc_info) {
+    struct emmc_info info, void_info;
+    memset(&info, 0, sizeof(struct emmc_info));
+    memset(&void_info, 0, sizeof(struct emmc_info));
+
+    if (access(EMMC_EXT_CSD_PATH, R_OK) >= 0) {
+        int emmc_fd = open(EMMC_EXT_CSD_PATH, O_RDONLY);
+        ASSERT_GE(emmc_fd, 0);
+        ASSERT_TRUE(parse_emmc_ecsd(emmc_fd, &info));
+        // parse_emmc_ecsd() should put something in info.
+        EXPECT_NE(0, memcmp(&void_info, &info, sizeof(struct emmc_info)));
+    }
+}
+
+static double mean(std::deque<uint32_t> nums) {
+    double sum = 0.0;
+    for (uint32_t i : nums) {
+    sum += i;
+    }
+    return sum / nums.size();
+}
+
+static double standard_deviation(std::deque<uint32_t> nums) {
+    double sum = 0.0;
+    double avg = mean(nums);
+    for (uint32_t i : nums) {
+    sum += ((double)i - avg) * ((double)i - avg);
+    }
+    return sqrt(sum / nums.size());
+}
+
+TEST(storaged_test, stream_stats) {
+    // 100 random numbers
+    std::vector<uint32_t> data = {8147,9058,1270,9134,6324,975,2785,5469,9575,9649,1576,9706,9572,4854,8003,1419,4218,9157,7922,9595,6557,357,8491,9340,6787,7577,7431,3922,6555,1712,7060,318,2769,462,971,8235,6948,3171,9502,344,4387,3816,7655,7952,1869,4898,4456,6463,7094,7547,2760,6797,6551,1626,1190,4984,9597,3404,5853,2238,7513,2551,5060,6991,8909,9593,5472,1386,1493,2575,8407,2543,8143,2435,9293,3500,1966,2511,6160,4733,3517,8308,5853,5497,9172,2858,7572,7537,3804,5678,759,540,5308,7792,9340,1299,5688,4694,119,3371};
+    std::deque<uint32_t> test_data;
+    stream_stats sstats;
+    for (uint32_t i : data) {
+        test_data.push_back(i);
+        sstats.add(i);
+
+        EXPECT_EQ((int)standard_deviation(test_data), (int)sstats.get_std());
+        EXPECT_EQ((int)mean(test_data), (int)sstats.get_mean());
+    }
+
+    for (uint32_t i : data) {
+        test_data.pop_front();
+        sstats.evict(i);
+
+        EXPECT_EQ((int)standard_deviation(test_data), (int)sstats.get_std());
+        EXPECT_EQ((int)mean(test_data), (int)sstats.get_mean());
+    }
+
+    // some real data
+    std::vector<uint32_t> another_data = {113875,81620,103145,28327,86855,207414,96526,52567,28553,250311};
+    test_data.clear();
+    uint32_t window_size = 2;
+    uint32_t idx;
+    stream_stats sstats1;
+    for (idx = 0; idx < window_size; ++idx) {
+        test_data.push_back(another_data[idx]);
+        sstats1.add(another_data[idx]);
+    }
+    EXPECT_EQ((int)standard_deviation(test_data), (int)sstats1.get_std());
+    EXPECT_EQ((int)mean(test_data), (int)sstats1.get_mean());
+    for (;idx < another_data.size(); ++idx) {
+        test_data.pop_front();
+        sstats1.evict(another_data[idx - window_size]);
+        test_data.push_back(another_data[idx]);
+        sstats1.add(another_data[idx]);
+        EXPECT_EQ((int)standard_deviation(test_data), (int)sstats1.get_std());
+        EXPECT_EQ((int)mean(test_data), (int)sstats1.get_mean());
+    }
+}
+
+static struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) {
+    struct disk_perf retval;
+    retval.read_perf = (double)perf.read_perf * mul;
+    retval.read_ios = (double)perf.read_ios * mul;
+    retval.write_perf = (double)perf.write_perf * mul;
+    retval.write_ios = (double)perf.write_ios * mul;
+    retval.queue = (double)perf.queue * mul;
+
+    return retval;
+}
+
+static struct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_stats stats2) {
+    struct disk_stats retval;
+    retval.read_ios = stats1.read_ios + stats2.read_ios;
+    retval.read_merges = stats1.read_merges + stats2.read_merges;
+    retval.read_sectors = stats1.read_sectors + stats2.read_sectors;
+    retval.read_ticks = stats1.read_ticks + stats2.read_ticks;
+    retval.write_ios = stats1.write_ios + stats2.write_ios;
+    retval.write_merges = stats1.write_merges + stats2.write_merges;
+    retval.write_sectors = stats1.write_sectors + stats2.write_sectors;
+    retval.write_ticks = stats1.write_ticks + stats2.write_ticks;
+    retval.io_in_flight = stats1.io_in_flight + stats2.io_in_flight;
+    retval.io_ticks = stats1.io_ticks + stats2.io_ticks;
+    retval.io_in_queue = stats1.io_in_queue + stats2.io_in_queue;
+    retval.end_time = stats1.end_time + stats2.end_time;
+
+    return retval;
+}
+
+TEST(storaged_test, disk_stats_monitor) {
+    // asserting that there is one file for diskstats
+    ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
+    // testing if detect() will return the right value
+    disk_stats_monitor dsm_detect;
+    // feed monitor with constant perf data for io perf baseline
+    // using constant perf is reasonable since the functionality of stream_stats
+    // has already been tested
+    struct disk_perf norm_perf = {
+        .read_perf = 10 * 1024,
+        .read_ios = 50,
+        .write_perf = 5 * 1024,
+        .write_ios = 25,
+        .queue = 5
+    };
+
+    std::random_device rd;
+    std::mt19937 gen(rd());
+    std::uniform_real_distribution<> rand(0.8, 1.2);
+
+    for (uint i = 0; i < dsm_detect.mWindow; ++i) {
+        struct disk_perf perf = disk_perf_multiply(norm_perf, rand(gen));
+
+        dsm_detect.add(&perf);
+        dsm_detect.mBuffer.push(perf);
+        EXPECT_EQ(dsm_detect.mBuffer.size(), (uint64_t)i + 1);
+    }
+
+    dsm_detect.mValid = true;
+    dsm_detect.update_mean();
+    dsm_detect.update_std();
+
+    for (double i = 0; i < 2 * dsm_detect.mSigma; i += 0.5) {
+        struct disk_perf test_perf;
+        struct disk_perf test_mean = dsm_detect.mMean;
+        struct disk_perf test_std = dsm_detect.mStd;
+
+        test_perf.read_perf = (double)test_mean.read_perf - i * test_std.read_perf;
+        test_perf.read_ios = (double)test_mean.read_ios - i * test_std.read_ios;
+        test_perf.write_perf = (double)test_mean.write_perf - i * test_std.write_perf;
+        test_perf.write_ios = (double)test_mean.write_ios - i * test_std.write_ios;
+        test_perf.queue = (double)test_mean.queue + i * test_std.queue;
+
+        EXPECT_EQ((i > dsm_detect.mSigma), dsm_detect.detect(&test_perf));
+    }
+
+    // testing if stalled disk_stats can be correctly accumulated in the monitor
+    disk_stats_monitor dsm_acc;
+    struct disk_stats norm_inc = {
+        .read_ios = 200,
+        .read_merges = 0,
+        .read_sectors = 200,
+        .read_ticks = 200,
+        .write_ios = 100,
+        .write_merges = 0,
+        .write_sectors = 100,
+        .write_ticks = 100,
+        .io_in_flight = 0,
+        .io_ticks = 600,
+        .io_in_queue = 300,
+        .start_time = 0,
+        .end_time = 100,
+        .counter = 0,
+        .io_avg = 0
+    };
+
+    struct disk_stats stall_inc = {
+        .read_ios = 200,
+        .read_merges = 0,
+        .read_sectors = 20,
+        .read_ticks = 200,
+        .write_ios = 100,
+        .write_merges = 0,
+        .write_sectors = 10,
+        .write_ticks = 100,
+        .io_in_flight = 0,
+        .io_ticks = 600,
+        .io_in_queue = 1200,
+        .start_time = 0,
+        .end_time = 100,
+        .counter = 0,
+        .io_avg = 0
+    };
+
+    struct disk_stats stats_base;
+    memset(&stats_base, 0, sizeof(stats_base));
+
+    int loop_size = 100;
+    for (int i = 0; i < loop_size; ++i) {
+        stats_base = disk_stats_add(stats_base, norm_inc);
+        dsm_acc.update(&stats_base);
+        EXPECT_EQ(dsm_acc.mValid, (uint32_t)(i + 1) >= dsm_acc.mWindow);
+        EXPECT_FALSE(dsm_acc.mStall);
+    }
+
+    stats_base = disk_stats_add(stats_base, stall_inc);
+    dsm_acc.update(&stats_base);
+    EXPECT_TRUE(dsm_acc.mValid);
+    EXPECT_TRUE(dsm_acc.mStall);
+
+    for (int i = 0; i < 10; ++i) {
+        stats_base = disk_stats_add(stats_base, norm_inc);
+        dsm_acc.update(&stats_base);
+        EXPECT_TRUE(dsm_acc.mValid);
+        EXPECT_FALSE(dsm_acc.mStall);
+    }
+}
+
+static void expect_increasing(struct disk_stats stats1, struct disk_stats stats2) {
+    EXPECT_LE(stats1.read_ios, stats2.read_ios);
+    EXPECT_LE(stats1.read_merges, stats2.read_merges);
+    EXPECT_LE(stats1.read_sectors, stats2.read_sectors);
+    EXPECT_LE(stats1.read_ticks, stats2.read_ticks);
+
+    EXPECT_LE(stats1.write_ios, stats2.write_ios);
+    EXPECT_LE(stats1.write_merges, stats2.write_merges);
+    EXPECT_LE(stats1.write_sectors, stats2.write_sectors);
+    EXPECT_LE(stats1.write_ticks, stats2.write_ticks);
+
+    EXPECT_LE(stats1.io_ticks, stats2.io_ticks);
+    EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue);
+}
+
+#define TEST_LOOPS 20
+TEST(storaged_test, disk_stats_publisher) {
+    // asserting that there is one file for diskstats
+    ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
+    disk_stats_publisher dsp;
+    struct disk_stats prev;
+    memset(&prev, 0, sizeof(prev));
+
+    for (int i = 0; i < TEST_LOOPS; ++i) {
+        dsp.update();
+        expect_increasing(prev, dsp.mPrevious);
+        prev = dsp.mPrevious;
+        pause(10);
+    }
+}
+
diff --git a/trusty/gatekeeper/trusty_gatekeeper.cpp b/trusty/gatekeeper/trusty_gatekeeper.cpp
index 7e55fb1..b3fbfa9 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.cpp
+++ b/trusty/gatekeeper/trusty_gatekeeper.cpp
@@ -19,9 +19,10 @@
 #include <assert.h>
 #include <errno.h>
 #include <stdio.h>
+
 #include <type_traits>
 
-#include <android/log.h>
+#include <log/log.h>
 
 #include "trusty_gatekeeper.h"
 #include "trusty_gatekeeper_ipc.h"
diff --git a/trusty/gatekeeper/trusty_gatekeeper_ipc.c b/trusty/gatekeeper/trusty_gatekeeper_ipc.c
index 45e65a7..f67944b 100644
--- a/trusty/gatekeeper/trusty_gatekeeper_ipc.c
+++ b/trusty/gatekeeper/trusty_gatekeeper_ipc.c
@@ -22,7 +22,7 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <android/log.h>
+#include <log/log.h>
 #include <trusty/tipc.h>
 
 #include "trusty_gatekeeper_ipc.h"
diff --git a/trusty/keymaster/trusty_keymaster_device.cpp b/trusty/keymaster/trusty_keymaster_device.cpp
index de5e463..1368f88 100644
--- a/trusty/keymaster/trusty_keymaster_device.cpp
+++ b/trusty/keymaster/trusty_keymaster_device.cpp
@@ -27,9 +27,9 @@
 
 #include <type_traits>
 
-#include <android/log.h>
 #include <hardware/keymaster0.h>
 #include <keymaster/authorization_set.h>
+#include <log/log.h>
 
 #include "trusty_keymaster_device.h"
 #include "trusty_keymaster_ipc.h"
diff --git a/trusty/keymaster/trusty_keymaster_ipc.c b/trusty/keymaster/trusty_keymaster_ipc.c
index 8755093..88546af 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.c
+++ b/trusty/keymaster/trusty_keymaster_ipc.c
@@ -23,7 +23,7 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <android/log.h>
+#include <log/log.h>
 #include <trusty/tipc.h>
 
 #include "trusty_keymaster_ipc.h"
diff --git a/trusty/libtrusty/trusty.c b/trusty/libtrusty/trusty.c
index 2398a53..a6238af 100644
--- a/trusty/libtrusty/trusty.c
+++ b/trusty/libtrusty/trusty.c
@@ -25,7 +25,7 @@
 #include <sys/ioctl.h>
 #include <unistd.h>
 
-#include <android/log.h>
+#include <log/log.h>
 
 #include "tipc_ioctl.h"
 
diff --git a/trusty/nvram/trusty_nvram_implementation.cpp b/trusty/nvram/trusty_nvram_implementation.cpp
index ddaf333..9215c85 100644
--- a/trusty/nvram/trusty_nvram_implementation.cpp
+++ b/trusty/nvram/trusty_nvram_implementation.cpp
@@ -22,8 +22,8 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <android/log.h>
 #include <hardware/nvram.h>
+#include <log/log.h>
 #include <trusty/tipc.h>
 
 #include <nvram/messages/blob.h>
diff --git a/trusty/storage/lib/storage.c b/trusty/storage/lib/storage.c
index 3002c0b..915bc17 100644
--- a/trusty/storage/lib/storage.c
+++ b/trusty/storage/lib/storage.c
@@ -23,7 +23,7 @@
 #include <string.h>
 #include <sys/uio.h>
 
-#include <android/log.h>
+#include <log/log.h>
 #include <trusty/tipc.h>
 #include <trusty/lib/storage.h>
 
diff --git a/trusty/storage/proxy/log.h b/trusty/storage/proxy/log.h
index 3d2e654..c81beab 100644
--- a/trusty/storage/proxy/log.h
+++ b/trusty/storage/proxy/log.h
@@ -16,5 +16,5 @@
 
 #define LOG_TAG "storageproxyd"
 
-#include <android/log.h>
+#include <log/log.h>
 
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
index c1ab2ac..769f0f5 100644
--- a/tzdatacheck/tzdatacheck.cpp
+++ b/tzdatacheck/tzdatacheck.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
 #include <errno.h>
 #include <ftw.h>
 #include <libgen.h>
@@ -29,47 +30,127 @@
 
 #include "android-base/logging.h"
 
+// The name of the file containing the distro version information.
+// See also libcore.tzdata.update2.TimeZoneDistro / libcore.tzdata.update2.DistroVersion.
+static const char* DISTRO_VERSION_FILENAME = "/distro_version";
+
+// distro_version is an ASCII file consisting of 17 bytes in the form: AAA.BBB|CCCCC|DDD
+// AAA.BBB is the major/minor version of the distro format (e.g. 001.001),
+// CCCCC is the rules version (e.g. 2016g)
+// DDD is the android revision for this rules version to allow for distro corrections (e.g. 001)
+// We only need the first 13 to determine if it is suitable for the device.
+static const int DISTRO_VERSION_LENGTH = 13;
+
+// The major version of the distro format supported by this code as a null-terminated char[].
+// See also libcore.tzdata.update2.TimeZoneDistro / libcore.tzdata.update2.DistroVersion.
+static const char SUPPORTED_DISTRO_MAJOR_VERSION[] = "001";
+
+// The length of the distro format major version excluding the \0
+static const size_t SUPPORTED_DISTRO_MAJOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MAJOR_VERSION) - 1;
+
+// The minor version of the distro format supported by this code as a null-terminated char[].
+// See also libcore.tzdata.update2.TimeZoneDistro / libcore.tzdata.update2.DistroVersion.
+static const char SUPPORTED_DISTRO_MINOR_VERSION[] = "001";
+
+// The length of the distro format minor version excluding the \0
+static const size_t SUPPORTED_DISTRO_MINOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MINOR_VERSION) - 1;
+
+// The length of the distro format version. e.g. 001.001
+static const size_t SUPPORTED_DISTRO_VERSION_LEN =
+        SUPPORTED_DISTRO_MAJOR_VERSION_LEN + SUPPORTED_DISTRO_MINOR_VERSION_LEN + 1;
+
+// The length of the IANA rules version bytes. e.g. 2016a
+static const size_t RULES_VERSION_LEN = 5;
+
+// Distro version bytes are: AAA.BBB|CCCCC - the rules version is CCCCC
+static const size_t DISTRO_VERSION_RULES_IDX = 8;
+
+// See also libcore.tzdata.update2.TimeZoneDistro.
 static const char* TZDATA_FILENAME = "/tzdata";
+
 // tzdata file header (as much as we need for the version):
 // byte[11] tzdata_version  -- e.g. "tzdata2012f"
 static const int TZ_HEADER_LENGTH = 11;
 
+static const char TZ_DATA_HEADER_PREFIX[] = "tzdata";
+static const size_t TZ_DATA_HEADER_PREFIX_LEN = sizeof(TZ_DATA_HEADER_PREFIX) - 1; // exclude \0
+
+
 static void usage() {
     std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
             "\n"
-            "Compares the headers of two tzdata files. If the one in SYSTEM_TZ_DIR is the\n"
-            "same or a higher version than the one in DATA_TZ_DIR the DATA_TZ_DIR is renamed\n"
-            "and then deleted.\n";
+            "Checks whether any timezone update distro in DATA_TZ_DIR is compatible with the\n"
+            "current Android release and better than or the same as base system timezone rules in\n"
+            "SYSTEM_TZ_DIR. If the timezone rules in SYSTEM_TZ_DIR are a higher version than the\n"
+            "one in DATA_TZ_DIR the DATA_TZ_DIR is renamed and then deleted.\n";
     exit(1);
 }
 
 /*
- * Opens a file and fills headerBytes with the first byteCount bytes from the file. It is a fatal
- * error if the file is too small or cannot be opened. If the file does not exist false is returned.
+ * Opens a file and fills buffer with the first byteCount bytes from the file.
+ * If the file does not exist or cannot be opened or is too short then false is returned.
  * If the bytes were read successfully then true is returned.
  */
-static bool readHeader(const std::string& tzDataFileName, char* headerBytes, size_t byteCount) {
-    FILE* tzDataFile = fopen(tzDataFileName.c_str(), "r");
-    if (tzDataFile == nullptr) {
-        if (errno == ENOENT) {
-            return false;
-        } else {
-            PLOG(FATAL) << "Error opening tzdata file " << tzDataFileName;
+static bool readBytes(const std::string& fileName, char* buffer, size_t byteCount) {
+    FILE* file = fopen(fileName.c_str(), "r");
+    if (file == nullptr) {
+        if (errno != ENOENT) {
+            PLOG(WARNING) << "Error opening file " << fileName;
         }
+        return false;
     }
-    size_t bytesRead = fread(headerBytes, 1, byteCount, tzDataFile);
+    size_t bytesRead = fread(buffer, 1, byteCount, file);
+    fclose(file);
     if (bytesRead != byteCount) {
-        LOG(FATAL) << tzDataFileName << " is too small. " << byteCount << " bytes required";
+        LOG(WARNING) << fileName << " is too small. " << byteCount << " bytes required";
+        return false;
     }
-    fclose(tzDataFile);
     return true;
 }
 
-/* Checks the contents of headerBytes. It is a fatal error if it not a tzdata header. */
-static void checkValidHeader(const std::string& fileName, char* headerBytes) {
+/*
+ * Checks the contents of headerBytes. Returns true if it is valid (starts with "tzdata"), false
+ * otherwise.
+ */
+static bool checkValidTzDataHeader(const std::string& fileName, const char* headerBytes) {
     if (strncmp("tzdata", headerBytes, 6) != 0) {
-        LOG(FATAL) << fileName << " does not start with the expected bytes (tzdata)";
+        LOG(WARNING) << fileName << " does not start with the expected bytes (tzdata)";
+        return false;
     }
+    return true;
+}
+
+static bool checkDigits(const char* buffer, const size_t count, size_t* i) {
+    for (size_t j = 0; j < count; j++) {
+      char toCheck = buffer[(*i)++];
+      if (!isdigit(toCheck)) {
+        return false;
+      }
+    }
+    return true;
+}
+
+static bool checkValidDistroVersion(const char* buffer) {
+    // See DISTRO_VERSION_LENGTH comments above for a description of the format.
+    size_t i = 0;
+    if (!checkDigits(buffer, 3, &i)) {
+      return false;
+    }
+    if (buffer[i++] != '.') {
+      return false;
+    }
+    if (!checkDigits(buffer, 3, &i)) {
+      return false;
+    }
+    if (buffer[i++] != '|') {
+      return false;
+    }
+    if (!checkDigits(buffer, 4, &i)) {
+      return false;
+    }
+    // Ignore the last character. It is assumed to be a letter but we don't check because it's not
+    // obvious what would happen at 'z'.
+    return true;
 }
 
 /* Return the parent directory of dirName. */
@@ -103,9 +184,24 @@
     return 0;
 }
 
+enum PathStatus { ERR, NONE, IS_DIR, NOT_DIR };
+
+static PathStatus checkPath(const std::string& path) {
+    struct stat buf;
+    if (stat(path.c_str(), &buf) != 0) {
+        if (errno != ENOENT) {
+            PLOG(WARNING) << "Unable to stat " << path;
+            return ERR;
+        }
+        return NONE;
+    }
+    return S_ISDIR(buf.st_mode) ? IS_DIR : NOT_DIR;
+}
+
 /*
  * Deletes dirToDelete and returns true if it is successful in removing or moving the directory out
- * of the way. If dirToDelete does not exist this function does nothing and returns true.
+ * of the way. If dirToDelete does not exist this function does nothing and returns true. If
+ * dirToDelete is not a directory or cannot be accessed this method returns false.
  *
  * During deletion, this function first renames the directory to a temporary name. If the temporary
  * directory cannot be created, or the directory cannot be renamed, false is returned. After the
@@ -114,23 +210,18 @@
  */
 static bool deleteDir(const std::string& dirToDelete) {
     // Check whether the dir exists.
-    struct stat buf;
-    if (stat(dirToDelete.c_str(), &buf) == 0) {
-      if (!S_ISDIR(buf.st_mode)) {
-        LOG(WARNING) << dirToDelete << " is not a directory";
+    int pathStatus = checkPath(dirToDelete);
+    if (pathStatus == NONE) {
+        LOG(INFO) << "Path " << dirToDelete << " does not exist";
+        return true;
+    }
+    if (pathStatus != IS_DIR) {
+        LOG(WARNING) << "Path " << dirToDelete << " failed to stat() or is not a directory.";
         return false;
-      }
-    } else {
-      if (errno == ENOENT) {
-          PLOG(INFO) << "Directory does not exist: " << dirToDelete;
-          return true;
-      } else {
-          PLOG(WARNING) << "Unable to stat " << dirToDelete;
-          return false;
-      }
     }
 
     // First, rename dirToDelete.
+
     std::string tempDirNameTemplate = getParentDir(dirToDelete);
     tempDirNameTemplate += "/tempXXXXXX";
 
@@ -142,7 +233,7 @@
         return false;
     }
 
-    // Rename dirToDelete to tempDirName.
+    // Rename dirToDelete to tempDirName (replacing the empty tempDirName directory created above).
     int rc = rename(dirToDelete.c_str(), &tempDirName[0]);
     if (rc == -1) {
         PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to "
@@ -151,6 +242,7 @@
     }
 
     // Recursively delete contents of tempDirName.
+
     rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */,
             FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
     if (rc == -1) {
@@ -160,9 +252,36 @@
 }
 
 /*
+ * Deletes the ConfigInstaller metadata directory.
+ * TODO(nfuller). http://b/31008728 Remove this when ConfigInstaller is no longer used.
+ */
+static void deleteConfigUpdaterMetadataDir(const char* dataZoneInfoDir) {
+    // Delete the update metadata
+    std::string dataUpdatesDirName(dataZoneInfoDir);
+    dataUpdatesDirName += "/updates";
+    LOG(INFO) << "Removing: " << dataUpdatesDirName;
+    bool deleted = deleteDir(dataUpdatesDirName);
+    if (!deleted) {
+        LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
+                << " was not successful";
+    }
+}
+
+/*
+ * Deletes the timezone update distro directory.
+ */
+static void deleteUpdateDistroDir(std::string& distroDirName) {
+    LOG(INFO) << "Removing: " << distroDirName;
+    bool deleted = deleteDir(distroDirName);
+    if (!deleted) {
+        LOG(WARNING) << "Deletion of distro dir " << distroDirName << " was not successful";
+    }
+}
+
+/*
  * After a platform update it is likely that timezone data found on the system partition will be
  * newer than the version found in the data partition. This tool detects this case and removes the
- * version in /data along with any update metadata.
+ * version in /data.
  *
  * Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The
  * paths for the metadata and current timezone data must match.
@@ -175,62 +294,126 @@
 int main(int argc, char* argv[]) {
     if (argc != 3) {
         usage();
+        return 1;
     }
 
     const char* systemZoneInfoDir = argv[1];
     const char* dataZoneInfoDir = argv[2];
 
+    // Check the distro directory exists. If it does not, exit quickly: nothing to do.
     std::string dataCurrentDirName(dataZoneInfoDir);
     dataCurrentDirName += "/current";
-    std::string dataTzDataFileName(dataCurrentDirName);
-    dataTzDataFileName += TZDATA_FILENAME;
-
-    std::vector<char> dataTzDataHeader;
-    dataTzDataHeader.reserve(TZ_HEADER_LENGTH);
-
-    bool dataFileExists = readHeader(dataTzDataFileName, dataTzDataHeader.data(), TZ_HEADER_LENGTH);
-    if (!dataFileExists) {
-        LOG(INFO) << "tzdata file " << dataTzDataFileName << " does not exist. No action required.";
+    int dataCurrentDirStatus = checkPath(dataCurrentDirName);
+    if (dataCurrentDirStatus == NONE) {
+        LOG(INFO) << "timezone distro dir " << dataCurrentDirName
+                << " does not exist. No action required.";
         return 0;
     }
-    checkValidHeader(dataTzDataFileName, dataTzDataHeader.data());
+    // If the distro directory path is not a directory or we can't stat() the path, exit with a
+    // warning: either there's a problem accessing storage or the world is not as it should be;
+    // nothing to do.
+    if (dataCurrentDirStatus != IS_DIR) {
+        LOG(WARNING) << "Current distro dir " << dataCurrentDirName
+                << " could not be accessed or is not a directory. result=" << dataCurrentDirStatus;
+        return 2;
+    }
 
+    // Check the installed distro version.
+    std::string distroVersionFileName(dataCurrentDirName);
+    distroVersionFileName += DISTRO_VERSION_FILENAME;
+    std::vector<char> distroVersion;
+    distroVersion.reserve(DISTRO_VERSION_LENGTH);
+    bool distroVersionReadOk =
+            readBytes(distroVersionFileName, distroVersion.data(), DISTRO_VERSION_LENGTH);
+    if (!distroVersionReadOk) {
+        LOG(WARNING) << "distro version file " << distroVersionFileName
+                << " does not exist or is too short. Deleting distro dir.";
+        // Implies the contents of the data partition is corrupt in some way. Try to clean up.
+        deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
+        deleteUpdateDistroDir(dataCurrentDirName);
+        return 3;
+    }
+
+    if (!checkValidDistroVersion(distroVersion.data())) {
+        LOG(WARNING) << "distro version file " << distroVersionFileName
+                << " is not valid. Deleting distro dir.";
+        // Implies the contents of the data partition is corrupt in some way. Try to clean up.
+        deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
+        deleteUpdateDistroDir(dataCurrentDirName);
+        return 4;
+    }
+
+    std::string actualDistroVersion =
+            std::string(distroVersion.data(), SUPPORTED_DISTRO_VERSION_LEN);
+    // Check the first 3 bytes of the distro version: these are the major version (e.g. 001).
+    // It must match the one we support exactly to be ok.
+    if (strncmp(
+            &distroVersion[0],
+            SUPPORTED_DISTRO_MAJOR_VERSION,
+            SUPPORTED_DISTRO_MAJOR_VERSION_LEN) != 0) {
+
+        LOG(INFO) << "distro version file " << distroVersionFileName
+                << " major version is not the required version " << SUPPORTED_DISTRO_MAJOR_VERSION
+                << ", was \"" << actualDistroVersion << "\". Deleting distro dir.";
+        // This implies there has been an OTA and the installed distro is not compatible with the
+        // new version of Android. Remove the installed distro.
+        deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
+        deleteUpdateDistroDir(dataCurrentDirName);
+        return 5;
+    }
+
+    // Check the last 3 bytes of the distro version: these are the minor version (e.g. 001).
+    // If the version in the distro is < the minor version required by this device it cannot be
+    // used.
+    if (strncmp(
+            &distroVersion[4],
+            SUPPORTED_DISTRO_MINOR_VERSION,
+            SUPPORTED_DISTRO_MINOR_VERSION_LEN) < 0) {
+
+        LOG(INFO) << "distro version file " << distroVersionFileName
+                << " minor version is not the required version " << SUPPORTED_DISTRO_MINOR_VERSION
+                << ", was \"" << actualDistroVersion << "\". Deleting distro dir.";
+        // This implies there has been an OTA and the installed distro is not compatible with the
+        // new version of Android. Remove the installed distro.
+        deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
+        deleteUpdateDistroDir(dataCurrentDirName);
+        return 5;
+    }
+
+    // Read the system rules version out of the /system tzdata file.
     std::string systemTzDataFileName(systemZoneInfoDir);
     systemTzDataFileName += TZDATA_FILENAME;
     std::vector<char> systemTzDataHeader;
     systemTzDataHeader.reserve(TZ_HEADER_LENGTH);
     bool systemFileExists =
-            readHeader(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH);
+            readBytes(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH);
     if (!systemFileExists) {
-        LOG(FATAL) << systemTzDataFileName << " does not exist or could not be opened";
+        // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
+        LOG(WARNING) << systemTzDataFileName << " does not exist or could not be opened";
+        return 6;
     }
-    checkValidHeader(systemTzDataFileName, systemTzDataHeader.data());
-
-    if (strncmp(&systemTzDataHeader[0], &dataTzDataHeader[0], TZ_HEADER_LENGTH) < 0) {
-        LOG(INFO) << "tzdata file " << dataTzDataFileName << " is the newer than "
-                << systemTzDataFileName << ". No action required.";
-    } else {
-        // We have detected the case this tool is intended to prevent. Go fix it.
-        LOG(INFO) << "tzdata file " << dataTzDataFileName << " is the same as or older than "
-                << systemTzDataFileName << "; fixing...";
-
-        // Delete the update metadata
-        std::string dataUpdatesDirName(dataZoneInfoDir);
-        dataUpdatesDirName += "/updates";
-        LOG(INFO) << "Removing: " << dataUpdatesDirName;
-        bool deleted = deleteDir(dataUpdatesDirName);
-        if (!deleted) {
-            LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
-                    << " was not successful";
-        }
-
-        // Delete the TZ data
-        LOG(INFO) << "Removing: " << dataCurrentDirName;
-        deleted = deleteDir(dataCurrentDirName);
-        if (!deleted) {
-            LOG(WARNING) << "Deletion of tzdata " << dataCurrentDirName << " was not successful";
-        }
+    if (!checkValidTzDataHeader(systemTzDataFileName, systemTzDataHeader.data())) {
+        // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
+        LOG(WARNING) << systemTzDataFileName << " does not have a valid header.";
+        return 7;
     }
 
+    // Compare the distro rules version against the system rules version.
+    if (strncmp(
+            &systemTzDataHeader[TZ_DATA_HEADER_PREFIX_LEN],
+            &distroVersion[DISTRO_VERSION_RULES_IDX],
+            RULES_VERSION_LEN) <= 0) {
+        LOG(INFO) << "Found an installed distro but it is valid. No action taken.";
+        // Implies there is an installed update, but it is good.
+        return 0;
+    }
+
+    // Implies there has been an OTA and the system version of the timezone rules is now newer
+    // than the version installed in /data. Remove the installed distro.
+    LOG(INFO) << "timezone distro in " << dataCurrentDirName << " is older than data in "
+            << systemTzDataFileName << "; fixing...";
+
+    deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
+    deleteUpdateDistroDir(dataCurrentDirName);
     return 0;
 }