Merge "libmetricslogger: Refactor Tron metrics histogram logging out of bootstat."
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 5a3b401..7afa616 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -313,3 +313,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..2b59034 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -21,6 +21,8 @@
 
 #include <android-base/macros.h>
 
+int usage(const char*, ...);
+
 void close_stdin();
 
 bool getcwd(std::string* cwd);
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index 9b59d05..b7e76a6 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -182,11 +182,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/usb_dispatch.cpp b/adb/client/usb_dispatch.cpp
index 597e66e..f02dccf 100644
--- a/adb/client/usb_dispatch.cpp
+++ b/adb/client/usb_dispatch.cpp
@@ -24,10 +24,10 @@
 
 void usb_init() {
     if (should_use_libusb()) {
-        LOG(INFO) << "using libusb backend";
+        LOG(DEBUG) << "using libusb backend";
         libusb::usb_init();
     } else {
-        LOG(INFO) << "using native backend";
+        LOG(DEBUG) << "using native backend";
         native::usb_init();
     }
 }
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 2befa0c..3b2df2e 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -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,11 +1922,11 @@
 
 
     /* "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;
     }
@@ -1961,12 +1957,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;
 }
 
diff --git a/adf/libadf/Android.bp b/adf/libadf/Android.bp
index 2b5461e..1a81a49 100644
--- a/adf/libadf/Android.bp
+++ b/adf/libadf/Android.bp
@@ -19,3 +19,5 @@
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
 }
+
+subdirs = ["tests"]
diff --git a/adf/libadf/include/video/adf.h b/adf/libadf/include/video/adf.h
new file mode 100644
index 0000000..77203a5
--- /dev/null
+++ b/adf/libadf/include/video/adf.h
@@ -0,0 +1,180 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   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,
+};
+struct adf_set_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u8 type;
+  __u8 enabled;
+};
+struct adf_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u8 type;
+  __u32 length;
+};
+struct adf_vsync_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  struct adf_event base;
+  __aligned_u64 timestamp;
+};
+struct adf_hotplug_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  struct adf_event base;
+  __u8 connected;
+};
+#define ADF_MAX_PLANES 4
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_buffer_config {
+  __u32 overlay_engine;
+  __u32 w;
+  __u32 h;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u32 format;
+  __s32 fd[ADF_MAX_PLANES];
+  __u32 offset[ADF_MAX_PLANES];
+  __u32 pitch[ADF_MAX_PLANES];
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u8 n_planes;
+  __s32 acquire_fence;
+};
+#define ADF_MAX_BUFFERS (4096 / sizeof(struct adf_buffer_config))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_post_config {
+  size_t n_interfaces;
+  __u32 __user * interfaces;
+  size_t n_bufs;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  struct adf_buffer_config __user * bufs;
+  size_t custom_data_size;
+  void __user * custom_data;
+  __s32 complete_fence;
+/* 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;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u16 h;
+  __u32 format;
+  __s32 fd;
+  __u32 offset;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u32 pitch;
+};
+struct adf_simple_post_config {
+  struct adf_buffer_config buf;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __s32 complete_fence;
+};
+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)
+#endif
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..c5d2e62
--- /dev/null
+++ b/adf/libadf/original-kernel-headers/video/adf.h
@@ -0,0 +1,321 @@
+/*
+ * 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,
+};
+
+/**
+ * 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
+ *
+ * @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;
+};
+#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
+ *
+ * @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_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)
+
+#endif /* _UAPI_VIDEO_ADF_H_ */
diff --git a/adf/libadf/tests/adf_test.cpp b/adf/libadf/tests/adf_test.cpp
index 01b2785..eaa9342 100644
--- a/adf/libadf/tests/adf_test.cpp
+++ b/adf/libadf/tests/adf_test.cpp
@@ -149,11 +149,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,
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 595239c..a7354a7 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -28,10 +28,12 @@
 #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>
 
@@ -218,6 +220,27 @@
   }
 }
 
+// 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");
+
+  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)) {
+      boot_event_store->AddBootEventWithValue(
+          "boottime.bootloader." + stageName, time_ms);
+    }
+  }
+}
+
 // 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() {
@@ -271,6 +294,8 @@
   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
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index ca8bea2..f4756d5 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -8,23 +8,24 @@
 #
 # 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 soft
-# reboot (e.g., adb shell stop && adb shell start).  /proc/uptime is not reset
-# during a soft reboot, which leads to false boot time metrics being reported.
+# 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 soft reboot, the flag is
+# 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 soft reboot. A soft reboot is
+# 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
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 6dc6675..ca881aa 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -195,3 +195,7 @@
 
     init_rc: ["tombstoned/tombstoned.rc"]
 }
+
+subdirs = [
+    "crasher",
+]
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index c145933..2889356 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -18,10 +18,12 @@
 #include <dirent.h>
 #include <fcntl.h>
 #include <stdlib.h>
-#include <syscall.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>
@@ -51,24 +53,25 @@
 using android::base::unique_fd;
 using android::base::StringPrintf;
 
-static bool pid_contains_tid(pid_t pid, pid_t tid) {
-  std::string task_path = StringPrintf("/proc/%d/task/%d", pid, tid);
-  return access(task_path.c_str(), F_OK) == 0;
+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(pid_t pid, pid_t tid, std::string* error) {
+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, tid)) {
+  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 %d", tid, pid);
+    *error = StringPrintf("thread %d is not in process", tid);
     return false;
   }
 
@@ -190,6 +193,24 @@
   _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";
+  }
+}
+
 static void check_process(int proc_fd, pid_t expected_pid) {
   android::procinfo::ProcessInfo proc_info;
   if (!android::procinfo::GetProcessInfoFromProcPidFd(proc_fd, &proc_info)) {
@@ -263,7 +284,7 @@
   check_process(target_proc_fd, target);
 
   std::string attach_error;
-  if (!ptrace_seize_thread(target, main_tid, &attach_error)) {
+  if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
     LOG(FATAL) << attach_error;
   }
 
@@ -304,6 +325,7 @@
   }
 
   int signo = siginfo.si_signo;
+  bool fatal_signal = signo != DEBUGGER_SIGNAL;
   bool backtrace = false;
   uintptr_t abort_address = 0;
 
@@ -319,17 +341,16 @@
 
   // Now that we have the signal that kicked things off, attach all of the
   // sibling threads, and then proceed.
-  bool fatal_signal = signo != DEBUGGER_SIGNAL;
-  std::set<pid_t> siblings;
   std::set<pid_t> attached_siblings;
-  if (fatal_signal) {
+  {
+    std::set<pid_t> siblings;
     if (!android::procinfo::GetProcessTids(target, &siblings)) {
       PLOG(FATAL) << "failed to get process siblings";
     }
     siblings.erase(main_tid);
 
     for (pid_t sibling_tid : siblings) {
-      if (!ptrace_seize_thread(target, sibling_tid, &attach_error)) {
+      if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) {
         LOG(WARNING) << attach_error;
       } else {
         attached_siblings.insert(sibling_tid);
@@ -337,20 +358,29 @@
     }
   }
 
+  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;
+  if (!backtrace) {
+    populate_open_files_list(target, &open_files);
+  }
+
+  // Drop our capabilities now that we've attached to the threads we care about.
+  drop_capabilities();
+
   check_process(target_proc_fd, target);
 
   // TODO: Use seccomp to lock ourselves down.
 
-  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(main_tid));
   std::string amfd_data;
 
   if (backtrace) {
     dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, attached_siblings, 0);
   } else {
-    // Collect the list of open files.
-    OpenFilesList open_files;
-    populate_open_files_list(target, &open_files);
-
     engrave_tombstone(output_fd.get(), backtrace_map.get(), open_files, target, main_tid,
                       attached_siblings, abort_address, fatal_signal ? &amfd_data : nullptr);
   }
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
new file mode 100644
index 0000000..4727894
--- /dev/null
+++ b/debuggerd/crasher/Android.bp
@@ -0,0 +1,81 @@
+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",
+    ],
+    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",
+    ],
+    multilib: {
+        lib32: {
+            stem: "static_crasher",
+        },
+        lib64: {
+            stem: "static_crasher64",
+        },
+    },
+}
diff --git a/debuggerd/crasher/Android.mk b/debuggerd/crasher/Android.mk
deleted file mode 100644
index b8b786b..0000000
--- a/debuggerd/crasher/Android.mk
+++ /dev/null
@@ -1,66 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-crasher_cppflags := \
-    -std=gnu++14 \
-    -W \
-    -Wall \
-    -Wextra \
-    -Wunused \
-    -Werror \
-    -O0 \
-    -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 := libbase 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 := libbase 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_handler libbase liblog
-
-include $(BUILD_EXECUTABLE)
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 288f116..64a38dd 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -36,6 +36,19 @@
 #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.
@@ -142,22 +155,36 @@
     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, "  nostack               crash with a NULL stack pointer\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");
@@ -235,6 +262,20 @@
                                                  MAP_SHARED | MAP_ANONYMOUS, -1, 0));
         munmap(map, sizeof(int));
         map[0] = '8';
+    } else if (!strcasecmp(arg, "seccomp")) {
+        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 {
         return usage();
     }
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 3208230..0c5d3cf 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -71,7 +71,7 @@
   std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
   bool backtrace = argc == 3;
   if (!debuggerd_trigger_dump(pid, std::move(pipewrite),
-                              backtrace ? kDebuggerdBacktrace : kDebuggerdBacktrace, 0)) {
+                              backtrace ? kDebuggerdBacktrace : kDebuggerdTombstone, 0)) {
     redirect_thread.join();
     errx(1, "failed to dump process %d", pid);
   }
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index e7503e9..002e940 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -40,10 +40,10 @@
 using android::base::unique_fd;
 
 #if defined(__LP64__)
-#define CRASHER_PATH  "/system/xbin/crasher64"
+#define CRASHER_PATH  "/system/bin/crasher64"
 #define ARCH_SUFFIX "64"
 #else
-#define CRASHER_PATH "/system/xbin/crasher"
+#define CRASHER_PATH "/system/bin/crasher"
 #define ARCH_SUFFIX ""
 #endif
 
@@ -192,7 +192,7 @@
   std::string type = "wait-" + crash_type;
   StartProcess([type]() {
     execl(CRASHER_PATH, CRASHER_PATH, type.c_str(), nullptr);
-    err(1, "exec failed");
+    exit(errno);
   });
 }
 
@@ -216,7 +216,9 @@
     FAIL() << "failed to wait for crasher: " << strerror(errno);
   }
 
-  if (!WIFSIGNALED(status)) {
+  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));
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 501d089..38a7be3 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -39,6 +39,7 @@
 #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>
@@ -67,11 +68,22 @@
 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.
-#define fatal(...)                                             \
-  do {                                                         \
-    __libc_format_log(ANDROID_LOG_FATAL, "libc", __VA_ARGS__); \
-    _exit(1);                                                  \
-  } while (0)
+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];
+  vsnprintf(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
@@ -83,6 +95,21 @@
  * 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) {
@@ -118,15 +145,6 @@
       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"
@@ -138,6 +156,7 @@
       __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);
 }
@@ -185,11 +204,11 @@
 
   int pipefds[2];
   if (pipe(pipefds) != 0) {
-    fatal("failed to create pipe");
+    fatal_errno("failed to create pipe");
   }
 
   // Don't use fork(2) to avoid calling pthread_atfork handlers.
-  int forkpid = clone(nullptr, nullptr, SIGCHLD, nullptr);
+  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));
@@ -198,11 +217,16 @@
     close(pipefds[0]);
     close(pipefds[1]);
 
+    // Set all of the ambient capability bits we can, so that crash_dump can ptrace us.
+    for (unsigned long i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) != -1; ++i) {
+      prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0);
+    }
+
     char buf[10];
     snprintf(buf, sizeof(buf), "%d", thread_info->crashing_tid);
     execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, buf, nullptr);
 
-    fatal("exec failed: %s", strerror(errno));
+    fatal_errno("exec failed");
   } else {
     close(pipefds[1]);
     char buf[4];
@@ -224,10 +248,12 @@
     close(pipefds[0]);
 
     // Don't leave a zombie child.
-    siginfo_t child_siginfo;
-    if (TEMP_FAILURE_RETRY(waitid(P_PID, forkpid, &child_siginfo, WEXITED)) != 0) {
+    int status;
+    if (TEMP_FAILURE_RETRY(waitpid(forkpid, &status, __WCLONE)) == -1 && errno != ECHILD) {
       __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;
     }
   }
@@ -257,7 +283,7 @@
   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("failed to resend signal during crash: %s", strerror(errno));
+      fatal_errno("failed to resend signal during crash");
     }
   }
 
@@ -329,7 +355,7 @@
           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("failed to spawn debuggerd dispatch thread: %s", strerror(errno));
+    fatal_errno("failed to spawn debuggerd dispatch thread");
   }
 
   // Wait for the child to start...
@@ -359,12 +385,12 @@
   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");
+    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("failed to mprotect debuggerd thread stack");
+    fatal_errno("failed to mprotect debuggerd thread stack");
   }
 
   // Stack grows negatively, set it to the last byte in the page...
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 01e9cf6..ac2c0b6 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -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";
@@ -195,6 +197,29 @@
   _LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
 }
 
+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 system call %d", si.si_syscall);
+  }
+
+  if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str());
+}
+
 static void dump_signal_info(log_t* log, pid_t tid) {
   siginfo_t si;
   memset(&si, 0, sizeof(si));
@@ -212,6 +237,8 @@
 
   _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_thread_info(log_t* log, pid_t pid, pid_t tid) {
@@ -262,11 +289,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);
@@ -277,7 +304,7 @@
       if (!func_name.empty()) {
         line += " (" + func_name;
         if (offset) {
-          line += android::base::StringPrintf("+%" PRIuPTR, offset);
+          line += StringPrintf("+%" PRIuPTR, offset);
         }
         line += ')';
       }
@@ -336,11 +363,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;
 }
@@ -426,8 +453,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;
@@ -441,7 +467,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());
   }
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index e7f1a07..a4a0b52 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -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;
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 032ae31..9069baa 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -302,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");
@@ -381,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/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 207f42b..be84e8a 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -76,7 +76,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;
     }
 
@@ -131,8 +131,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++) {
@@ -140,10 +140,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);
             }
         }
@@ -153,10 +155,10 @@
          * (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),
                                           const_cast<char **>(e2fsck_argv),
@@ -167,7 +169,7 @@
 
             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")) {
@@ -176,7 +178,7 @@
                     "-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),
                                       const_cast<char **>(f2fs_fsck_argv),
@@ -185,7 +187,7 @@
                                       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;
         }
     }
 
@@ -231,8 +233,8 @@
          * 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 {
             const char* arg1 = nullptr;
             const char* arg2 = nullptr;
@@ -244,7 +246,7 @@
                 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));
+                    PERROR << "Can't read '" << blk_device << "' super block";
                     return force_check;
                 }
 
@@ -253,20 +255,20 @@
                 int want_quota = fs_mgr_is_quota(rec) != 0;
 
                 if (has_quota == want_quota) {
-                    INFO("Requested quota status is match on %s\n", blk_device);
+                    LINFO << "Requested quota status is match on " << blk_device;
                     return force_check;
                 } else if (want_quota) {
-                    INFO("Enabling quota on %s\n", blk_device);
+                    LINFO << "Enabling quota on " << blk_device;
                     arg1 = "-Oquota";
                     arg2 = "-Qusrquota,grpquota";
                     force_check = 1;
                 } else {
-                    INFO("Disabling quota on %s\n", blk_device);
+                    LINFO << "Disabling quota on " << blk_device;
                     arg1 = "-Q^usrquota,^grpquota";
                     arg2 = "-O^quota";
                 }
             } else {
-                ERROR("Failed to open '%s': %s\n", blk_device, strerror(errno));
+                PERROR << "Failed to open '" << blk_device << "'";
                 return force_check;
             }
 
@@ -282,7 +284,7 @@
                                           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;
             }
         }
     }
@@ -298,10 +300,10 @@
          * 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;
@@ -312,22 +314,23 @@
                 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));
+                    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");
+                    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;
             }
 
@@ -346,7 +349,7 @@
 
             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;
             }
         }
     }
@@ -406,7 +409,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);
     }
@@ -488,8 +492,11 @@
              * 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;
             }
 
@@ -510,9 +517,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 */
@@ -547,14 +556,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;
     }
 
@@ -568,7 +577,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;
         }
 
@@ -583,7 +592,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;
         }
 
@@ -591,11 +600,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;
@@ -638,13 +648,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;
@@ -717,7 +727,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;
             }
         }
@@ -733,20 +743,20 @@
              * mount_with_alternatives().
              */
             if (avb_ret == FS_MGR_SETUP_AVB_HASHTREE_DISABLED) {
-                INFO("AVB HASHTREE disabled\n");
+                LINFO << "AVB HASHTREE disabled";
             } else if (fs_mgr_setup_avb(&fstab->recs[i]) !=
                        FS_MGR_SETUP_AVB_SUCCESS) {
-                ERROR("Failed to set up AVB on partition: %s, skipping!\n",
-                      fstab->recs[i].mount_point);
+                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) && device_is_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;
             }
         }
@@ -770,7 +780,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;
             }
@@ -788,19 +798,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)) {
@@ -811,7 +823,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;
             }
@@ -819,18 +832,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;
@@ -839,15 +856,15 @@
             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;
@@ -899,8 +916,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;
         }
 
@@ -928,20 +945,20 @@
              * mount_with_alternatives().
              */
             if (avb_ret == FS_MGR_SETUP_AVB_HASHTREE_DISABLED) {
-                INFO("AVB HASHTREE disabled\n");
+                LINFO << "AVB HASHTREE disabled";
             } else if (fs_mgr_setup_avb(&fstab->recs[i]) !=
                        FS_MGR_SETUP_AVB_SUCCESS) {
-                ERROR("Failed to set up AVB on partition: %s, skipping!\n",
-                      fstab->recs[i].mount_point);
+                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) && device_is_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;
             }
         }
@@ -962,8 +979,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 {
@@ -971,7 +988,8 @@
         }
     } 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:
@@ -992,7 +1010,7 @@
     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;
     }
 
@@ -1011,7 +1029,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++;
@@ -1057,7 +1076,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;
               }
@@ -1067,7 +1087,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;
             }
@@ -1086,7 +1106,7 @@
                                       &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;
         }
@@ -1102,7 +1122,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;
         }
     }
@@ -1159,7 +1179,7 @@
     if ((fstab_rec->fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
         int rc = fs_mgr_setup_verity(fstab_rec, false);
         if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
-            INFO("Verity disabled");
+            LINFO << "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;
@@ -1167,7 +1187,7 @@
             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");
+        LERROR << "Verity must be enabled for early mounted partitions on secured devices";
         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
index 51632cf..dd08271 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -209,21 +209,21 @@
         expected_digest_size = SHA512_DIGEST_LENGTH * 2;
         vbmeta_prop->hash_alg = kSHA512;
     } else {
-        ERROR("Unknown hash algorithm: %s\n", hash_alg.c_str());
+        LERROR << "Unknown hash algorithm: " << hash_alg.c_str();
         return false;
     }
 
     // Reads digest.
     if (digest.size() != expected_digest_size) {
-        ERROR("Unexpected digest size: %zu (expected %zu)\n", 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)) {
-        ERROR("Hash digest contains non-hexidecimal character: %s\n",
-              digest.c_str());
+        LERROR << "Hash digest contains non-hexidecimal character: "
+               << digest.c_str();
         return false;
     }
 
@@ -252,7 +252,7 @@
                                  const androidboot_vbmeta &vbmeta_prop)
 {
     if (verify_data.num_vbmeta_images == 0) {
-        ERROR("No vbmeta images\n");
+        LERROR << "No vbmeta images";
         return false;
     }
 
@@ -268,13 +268,13 @@
     }
 
     if (total_size != vbmeta_prop.vbmeta_size) {
-        ERROR("total vbmeta size mismatch: %zu (expected: %zu)\n", total_size,
-              vbmeta_prop.vbmeta_size);
+        LERROR << "total vbmeta size mismatch: " << total_size
+               << " (expected: " << vbmeta_prop.vbmeta_size << ")";
         return false;
     }
 
     if (!digest_matched) {
-        ERROR("vbmeta digest mismatch\n");
+        LERROR << "vbmeta digest mismatch";
         return false;
     }
 
@@ -326,11 +326,11 @@
     }
 
     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;
     }
 
-    INFO("loading verity table: '%s'", verity_params);
+    LINFO << "Loading verity table: '" << verity_params << "'";
 
     // Sets ext target boundary.
     verity_params += strlen(verity_params) + 1;
@@ -339,7 +339,7 @@
 
     // Sends 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 false;
     }
 
@@ -354,7 +354,7 @@
     // Gets the device mapper fd.
     android::base::unique_fd fd(open("/dev/device-mapper", O_RDWR));
     if (fd < 0) {
-        ERROR("Error opening device mapper (%s)\n", strerror(errno));
+        PERROR << "Error opening device mapper";
         return false;
     }
 
@@ -363,14 +363,14 @@
     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)) {
-        ERROR("Couldn't create verity device!\n");
+        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)) {
-        ERROR("Couldn't get verity device number!\n");
+        LERROR << "Couldn't get verity device number!";
         return false;
     }
 
@@ -378,7 +378,7 @@
     if (!hashtree_load_verity_table(io, mount_point, fd,
                                     std::string(fstab_entry->blk_device),
                                     hashtree_desc, salt, root_digest)) {
-        ERROR("Couldn't load verity table!\n");
+        LERROR << "Couldn't load verity table!";
         return false;
     }
 
@@ -432,16 +432,16 @@
             verify_data.vbmeta_images[i].partition_name);
         if (vbmeta_partition_name != "vbmeta" &&
             vbmeta_partition_name != partition_name) {
-            WARNING("Skip vbmeta image at %s for partition: %s\n",
-                    verify_data.vbmeta_images[i].partition_name,
-                    partition_name.c_str());
+            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)) {
-                WARNING("Descriptor is invalid.\n");
+                LWARNING << "Descriptor[" << j << "] is invalid";
                 continue;
             }
             if (desc.tag == AVB_DESCRIPTOR_TAG_HASHTREE) {
@@ -468,7 +468,7 @@
     }
 
     if (!found) {
-        ERROR("Partition descriptor not found: %s\n", partition_name.c_str());
+        LERROR << "Partition descriptor not found: " << partition_name.c_str();
         return false;
     }
 
@@ -482,22 +482,6 @@
     return true;
 }
 
-static inline bool polling_vbmeta_blk_device(struct fstab *fstab)
-{
-    // It needs the block device symlink: fstab_rec->blk_device to read
-    // /vbmeta partition. However, the symlink created by ueventd might
-    // not be ready at this point. Use test_access() to poll it before
-    // trying to read the partition.
-    struct fstab_rec *fstab_entry =
-        fs_mgr_get_entry_for_mount_point(fstab, "/vbmeta");
-
-    // Makes sure /vbmeta block device is ready to access.
-    if (fs_mgr_test_access(fstab_entry->blk_device) < 0) {
-        return false;
-    }
-    return true;
-}
-
 static bool init_is_avb_used()
 {
     // When AVB is used, boot loader should set androidboot.vbmeta.{hash_alg,
@@ -529,11 +513,6 @@
 {
     FS_MGR_CHECK(fstab != nullptr);
 
-    if (!polling_vbmeta_blk_device(fstab)) {
-        ERROR("Failed to find block device of /vbmeta\n");
-        return FS_MGR_SETUP_AVB_FAIL;
-    }
-
     // Gets the expected hash value of vbmeta images from
     // kernel cmdline.
     if (!load_vbmeta_prop(&fs_mgr_vbmeta_prop)) {
@@ -542,7 +521,7 @@
 
     fs_mgr_avb_ops = fs_mgr_dummy_avb_ops_new(fstab);
     if (fs_mgr_avb_ops == nullptr) {
-        ERROR("Failed to allocate dummy avb_ops\n");
+        LERROR << "Failed to allocate dummy avb_ops";
         return FS_MGR_SETUP_AVB_FAIL;
     }
 
@@ -562,17 +541,17 @@
     //   - 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) {
-            ERROR("ERROR_VERIFICATION isn't allowed\n");
+            LERROR << "ERROR_VERIFICATION isn't allowed";
             goto fail;
         }
     } else if (verify_result != AVB_SLOT_VERIFY_RESULT_OK) {
-        ERROR("avb_slot_verify failed, result: %d\n", verify_result);
+        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)) {
-        ERROR("verify_vbmeta_images failed\n");
+        LERROR << "verify_vbmeta_images failed";
         goto fail;
     } else {
         // Checks whether FLAGS_HASHTREE_DISABLED is set.
@@ -619,8 +598,8 @@
     std::string partition_name(basename(fstab_entry->mount_point));
     if (!avb_validate_utf8((const uint8_t *)partition_name.c_str(),
                            partition_name.length())) {
-        ERROR("Partition name: %s is not valid UTF-8.\n",
-              partition_name.c_str());
+        LERROR << "Partition name: " << partition_name.c_str()
+               << " is not valid UTF-8.";
         return FS_MGR_SETUP_AVB_FAIL;
     }
 
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index f3030eb..f96f124 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -66,7 +66,7 @@
         fs_mgr_get_entry_for_mount_point(fs_mgr_fstab, "/misc");
 
     if (fstab_entry == nullptr) {
-        ERROR("Partition (%s) not found in fstab\n", partition);
+        LERROR << "/misc mount point not found in fstab";
         return AVB_IO_RESULT_ERROR_IO;
     }
 
@@ -79,11 +79,18 @@
         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) {
-        ERROR("Failed to open %s (%s)\n", path.c_str(), strerror(errno));
+        PERROR << "Failed to open " << path.c_str();
         return AVB_IO_RESULT_ERROR_IO;
     }
 
@@ -92,13 +99,13 @@
     if (offset < 0) {
         off64_t total_size = lseek64(fd, 0, SEEK_END);
         if (total_size == -1) {
-            ERROR("Failed to lseek64 to end of the partition\n");
+            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) {
-            ERROR("Failed to lseek64 to the beginning of the partition\n");
+            LERROR << "Failed to lseek64 to the beginning of the partition";
             return AVB_IO_RESULT_ERROR_IO;
         }
     }
@@ -109,8 +116,8 @@
         TEMP_FAILURE_RETRY(pread64(fd, buffer, num_bytes, offset));
 
     if (num_read < 0 || (size_t)num_read != num_bytes) {
-        ERROR("Failed to read %zu bytes from %s offset %" PRId64 " (%s)\n",
-              num_bytes, path.c_str(), offset, strerror(errno));
+        PERROR << "Failed to read " << num_bytes << " bytes from "
+               << path.c_str() << " offset " << offset;
         return AVB_IO_RESULT_ERROR_IO;
     }
 
@@ -184,7 +191,7 @@
 
     ops = (AvbOps *)calloc(1, sizeof(AvbOps));
     if (ops == nullptr) {
-        ERROR("Error allocating memory for AvbOps.\n");
+        LERROR << "Error allocating memory for AvbOps";
         return nullptr;
     }
 
diff --git a/fs_mgr/fs_mgr_dm_ioctl.cpp b/fs_mgr/fs_mgr_dm_ioctl.cpp
index 939657e..6012a39 100644
--- a/fs_mgr/fs_mgr_dm_ioctl.cpp
+++ b/fs_mgr/fs_mgr_dm_ioctl.cpp
@@ -45,7 +45,7 @@
 {
     fs_mgr_verity_ioctl_init(io, name, 1);
     if (ioctl(fd, DM_DEV_CREATE, io)) {
-        ERROR("Error creating device mapping (%s)", strerror(errno));
+        PERROR << "Error creating device mapping";
         return false;
     }
     return true;
@@ -57,7 +57,7 @@
 {
     fs_mgr_verity_ioctl_init(io, name, 0);
     if (ioctl(fd, DM_DEV_REMOVE, io)) {
-        ERROR("Error removing device mapping (%s)", strerror(errno));
+        PERROR << "Error removing device mapping";
         return false;
     }
     return true;
@@ -72,7 +72,7 @@
 
     fs_mgr_verity_ioctl_init(io, name, 0);
     if (ioctl(fd, DM_DEV_STATUS, io)) {
-        ERROR("Error fetching verity device number (%s)", strerror(errno));
+        PERROR << "Error fetching verity device number";
         return false;
     }
 
@@ -88,7 +88,7 @@
 {
     fs_mgr_verity_ioctl_init(io, name, 0);
     if (ioctl(fd, DM_DEV_SUSPEND, io)) {
-        ERROR("Error activating verity device (%s)", strerror(errno));
+        PERROR << "Error activating verity device";
         return false;
     }
     return true;
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 45298b3..5705f93 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -45,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;
     }
@@ -58,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;
     }
@@ -73,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);
 
@@ -106,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;
@@ -128,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.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 23059df..48ddf29 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -196,7 +196,7 @@
                         }
                     }
                     if (flag_vals->file_encryption_mode == 0) {
-                        ERROR("Unknown file encryption mode: %s\n", mode);
+                        LERROR << "Unknown file encryption mode: " << mode;
                     }
                 } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
                     /* The length flag is followed by an = and the
@@ -226,7 +226,7 @@
                             flag_vals->partnum = strtol(part_start, NULL, 0);
                         }
                     } else {
-                        ERROR("Warning: voldmanaged= flag malformed\n");
+                        LERROR << "Warning: voldmanaged= flag malformed";
                     }
                 } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
                     flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
@@ -276,7 +276,7 @@
                 /* fs_options was not passed in, so if the flag is unknown
                  * it's an error.
                  */
-                ERROR("Warning: unknown flag %s\n", p);
+                LERROR << "Warning: unknown flag " << p;
             }
         }
         p = strtok_r(NULL, ",", &savep);
@@ -321,7 +321,7 @@
     }
 
     if (!entries) {
-        ERROR("No entries found in fstab\n");
+        LERROR << "No entries found in fstab";
         goto err;
     }
 
@@ -354,30 +354,30 @@
          * between the two reads.
          */
         if (cnt >= entries) {
-            ERROR("Tried to process more entries than counted\n");
+            LERROR << "Tried to process more entries than counted";
             break;
         }
 
         if (!(p = strtok_r(line, delim, &save_ptr))) {
-            ERROR("Error parsing mount source\n");
+            LERROR << "Error parsing mount source";
             goto err;
         }
         fstab->recs[cnt].blk_device = strdup(p);
 
         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
-            ERROR("Error parsing mount_point\n");
+            LERROR << "Error parsing mount_point";
             goto err;
         }
         fstab->recs[cnt].mount_point = strdup(p);
 
         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
-            ERROR("Error parsing fs_type\n");
+            LERROR << "Error parsing fs_type";
             goto err;
         }
         fstab->recs[cnt].fs_type = strdup(p);
 
         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
-            ERROR("Error parsing mount_flags\n");
+            LERROR << "Error parsing mount_flags";
             goto err;
         }
         tmp_fs_options[0] = '\0';
@@ -392,7 +392,7 @@
         }
 
         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
-            ERROR("Error parsing fs_mgr_options\n");
+            LERROR << "Error parsing fs_mgr_options";
             goto err;
         }
         fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
@@ -413,7 +413,7 @@
     }
     /* 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");
+        LERROR << "Error updating for slotselect";
         goto err;
     }
     free(line);
@@ -433,7 +433,7 @@
 
     fstab_file = fopen(fstab_path, "r");
     if (!fstab_file) {
-        ERROR("Cannot open file %s\n", fstab_path);
+        LERROR << "Cannot open file " << fstab_path;
         return NULL;
     }
     fstab = fs_mgr_read_fstab_file(fstab_file);
diff --git a/fs_mgr/fs_mgr_main.cpp b/fs_mgr/fs_mgr_main.cpp
index f2901f3..f3919d9 100644
--- a/fs_mgr/fs_mgr_main.cpp
+++ b/fs_mgr/fs_mgr_main.cpp
@@ -27,7 +27,8 @@
 
 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);
 }
 
@@ -88,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);
 
@@ -104,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 0a27c7a..79c27c4 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -17,11 +17,9 @@
 #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>
 
-#ifdef __cplusplus
-#include <android-base/logging.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.
@@ -29,14 +27,21 @@
  * indicate the check happens in fs_mgr.
  */
 #define FS_MGR_CHECK(x) CHECK(x) << "in libfs_mgr "
-#endif
+
+#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
 
 __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
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 0f59115..94b43e4 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -48,9 +48,8 @@
         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));
+                PERROR << "Error opening misc partition '"
+                       << fstab->recs[n].blk_device << "'";
                 return -1;
             } else {
                 break;
@@ -59,7 +58,7 @@
     }
 
     if (misc_fd == -1) {
-        ERROR("Error finding misc partition\n");
+        LERROR << "Error finding misc partition";
         return -1;
     }
 
@@ -67,7 +66,7 @@
     // 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));
+        PERROR << "Error reading bootloader_message";
         close(misc_fd);
         return -1;
     }
@@ -98,11 +97,11 @@
     // 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);
+        LINFO << "Using slot suffix '" << out_suffix << "' from misc";
         return 0;
     }
 
-    ERROR("Error determining slot_suffix\n");
+    LERROR << "Error determining slot_suffix";
 
     return -1;
 }
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index f6a14da..1ec4540 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -94,12 +94,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;
     }
@@ -108,7 +108,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;
     }
 
@@ -128,14 +128,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;
     }
 
@@ -227,7 +227,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;
     }
 
@@ -246,7 +246,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;
     }
 
@@ -277,11 +277,11 @@
     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;
@@ -290,7 +290,7 @@
 
     // 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;
     }
 
@@ -309,13 +309,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;
     }
 
@@ -326,14 +326,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;
     }
 
@@ -405,14 +403,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;
     }
 
@@ -421,13 +419,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;
@@ -452,14 +450,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;
         }
@@ -483,13 +480,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;
     }
 
@@ -512,13 +509,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;
     }
 
@@ -530,13 +527,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;
     }
 
@@ -558,15 +555,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;
@@ -590,15 +586,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;
     }
 
@@ -606,7 +600,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;
     }
 
@@ -618,14 +612,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;
     }
 
@@ -635,8 +629,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;
         }
     }
@@ -654,7 +648,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;
     }
 
@@ -720,7 +714,7 @@
     fstab = fs_mgr_read_fstab(fstab_filename);
 
     if (!fstab) {
-        ERROR("Failed to read %s\n", fstab_filename);
+        LERROR << "Failed to read " << fstab_filename;
         goto out;
     }
 
@@ -776,7 +770,7 @@
     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;
     }
 
@@ -789,7 +783,7 @@
     fstab = fs_mgr_read_fstab(fstab_filename);
 
     if (!fstab) {
-        ERROR("Failed to read %s\n", fstab_filename);
+        LERROR << "Failed to read " << fstab_filename;
         goto out;
     }
 
@@ -810,8 +804,8 @@
             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.c_str(), strerror(errno));
+                PERROR << "Failed to query DM_TABLE_STATUS for "
+                       << mount_point.c_str();
                 continue;
             }
         }
@@ -881,22 +875,20 @@
 
     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 << "'";
         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
@@ -910,19 +902,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 (!fs_mgr_create_verity_device(io, mount_point, fd)) {
-        ERROR("Couldn't create verity device!\n");
+        LERROR << "Couldn't create verity device!";
         goto out;
     }
 
     // get the name of the device file
     if (!fs_mgr_get_verity_device_name(io, mount_point, fd, &verity_blk_name)) {
-        ERROR("Couldn't get verity device number!\n");
+        LERROR << "Couldn't get verity device number!";
         goto out;
     }
 
@@ -957,8 +949,8 @@
         }
     }
 
-    INFO("Enabling dm-verity for %s (mode %d)\n",
-         mount_point.c_str(), 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
@@ -973,7 +965,7 @@
 
     if (params.ecc.valid) {
         // kernel may not support error correction, try without
-        INFO("Disabling error correction for %s\n", mount_point.c_str());
+        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,
@@ -990,7 +982,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.c_str());
+        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,
@@ -999,7 +991,7 @@
         }
     }
 
-    ERROR("Failed to load verity table for %s\n", mount_point.c_str());
+    LERROR << "Failed to load verity table for " << mount_point.c_str();
     goto out;
 
 loaded:
@@ -1015,10 +1007,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);
+        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;
         }
     }
@@ -1028,7 +1021,7 @@
         free(fstab->blk_device);
         fstab->blk_device = strdup(verity_blk_name.c_str());
     } else if (!fs_mgr_destroy_verity_device(io, mount_point, fd)) {
-        ERROR("Failed to remove verity device %s\n", mount_point.c_str());
+        LERROR << "Failed to remove verity device " << mount_point.c_str();
         goto out;
     }
 
diff --git a/include/android b/include/android
deleted file mode 120000
index 4872393..0000000
--- a/include/android
+++ /dev/null
@@ -1 +0,0 @@
-../liblog/include/android
\ No newline at end of file
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/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 759be52..a10a714 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -70,7 +70,6 @@
     init.cpp \
     keychords.cpp \
     property_service.cpp \
-    seccomp.cpp \
     signal_handler.cpp \
     ueventd.cpp \
     ueventd_parser.cpp \
@@ -97,7 +96,6 @@
     libbase \
     libc \
     libselinux \
-    libseccomp_policy \
     liblog \
     libcrypto_utils \
     libcrypto \
@@ -136,3 +134,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
index cef0dbc..c76a33b 100644
--- a/init/README.md
+++ b/init/README.md
@@ -298,7 +298,8 @@
 > 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.
+  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
@@ -412,6 +413,11 @@
   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,
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 1186e9d..2388edc 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -265,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;
 }
 
@@ -1003,6 +1007,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
  */
@@ -1074,6 +1101,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/init.cpp b/init/init.cpp
index e7772e7..43f601f 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -62,7 +62,6 @@
 #include "keychords.h"
 #include "log.h"
 #include "property_service.h"
-#include "seccomp.h"
 #include "service.h"
 #include "signal_handler.h"
 #include "ueventd.h"
@@ -83,10 +82,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;
@@ -128,10 +131,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()
@@ -793,12 +838,6 @@
 
         // Now set up SELinux for second stage.
         selinux_initialize(false);
-
-        // Install system-wide seccomp filter
-        if (!set_seccomp_filter()) {
-            LOG(ERROR) << "Failed to set seccomp policy";
-            security_failure();
-        }
     }
 
     // These directories were necessarily created before initial policy load
@@ -811,7 +850,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");
@@ -875,7 +915,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();
         }
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/property_service.cpp b/init/property_service.cpp
index 7e11ff0..ce197ee 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -575,16 +575,16 @@
 }
 
 void property_load_boot_defaults() {
-    load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
-    load_properties_from_file(PROP_PATH_ODM_DEFAULT, NULL);
-    load_properties_from_file(PROP_PATH_VENDOR_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);
         }
     }
 }
@@ -639,10 +639,10 @@
 }
 
 void load_system_props() {
-    load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
-    load_properties_from_file(PROP_PATH_ODM_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();
 }
 
diff --git a/init/seccomp.cpp b/init/seccomp.cpp
deleted file mode 100644
index 92f30e8..0000000
--- a/init/seccomp.cpp
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "seccomp.h"
-
-#include <vector>
-
-#include <sys/prctl.h>
-
-#include <linux/unistd.h>
-#include <linux/audit.h>
-#include <linux/filter.h>
-#include <linux/seccomp.h>
-
-#include "log.h"
-#include "seccomp_policy.h"
-
-#define syscall_nr (offsetof(struct seccomp_data, nr))
-#define arch_nr (offsetof(struct seccomp_data, arch))
-
-#if   defined __arm__
-#define AUDIT_ARCH_NR AUDIT_ARCH_ARM
-#elif defined __aarch64__
-#define AUDIT_ARCH_NR AUDIT_ARCH_AARCH64
-#define AUDIT_ARCH_NR32 AUDIT_ARCH_ARM
-#elif defined __i386__
-#define AUDIT_ARCH_NR AUDIT_ARCH_I386
-#elif defined __x86_64__
-#define AUDIT_ARCH_NR AUDIT_ARCH_X86_64
-#define AUDIT_ARCH_NR32 AUDIT_ARCH_I386
-#elif defined __mips64__
-#define AUDIT_ARCH_NR AUDIT_ARCH_MIPS64
-#define AUDIT_ARCH_NR32 AUDIT_ARCH_MIPS
-#elif defined __mips__ && !defined __mips64__
-#define AUDIT_ARCH_NR AUDIT_ARCH_MIPS
-#else
-#error "Could not determine AUDIT_ARCH_NR for this architecture"
-#endif
-
-typedef std::vector<sock_filter> filter;
-
-// We want to keep the below inline functions for debugging and future
-// development even though they are not used currently.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-function"
-
-static inline void Kill(filter& f) {
-    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL));
-}
-
-static inline void Trap(filter& f) {
-    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRAP));
-}
-
-static inline void Error(filter& f, __u16 retcode) {
-    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO + retcode));
-}
-
-inline static void Trace(filter& f) {
-    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE));
-}
-
-inline static void Allow(filter& f) {
-    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW));
-}
-
-inline static void AllowSyscall(filter& f, __u32 num) {
-    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, num, 0, 1));
-    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW));
-}
-
-inline static void ExamineSyscall(filter& f) {
-    f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_nr));
-}
-
-#ifdef AUDIT_ARCH_NR32
-inline static int SetValidateArchitectureJumpTarget(size_t offset, filter& f) {
-    auto jump_length = f.size() - offset - 1;
-    auto u8_jump_length = (__u8) jump_length;
-    if (u8_jump_length != jump_length) {
-        LOG(ERROR) << "Can't set jump greater than 255 - actual jump is " << jump_length;
-        return -1;
-    }
-    f[offset] = BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_NR32, u8_jump_length, 0);
-    return 0;
-}
-#endif
-
-inline static size_t ValidateArchitectureAndJumpIfNeeded(filter& f) {
-    f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, arch_nr));
-
-#ifdef AUDIT_ARCH_NR32
-    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_NR, 2, 0));
-    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_NR32, 1, 0));
-    Kill(f);
-    return f.size() - 2;
-#else
-    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_NR, 1, 0));
-    Kill(f);
-    return 0;
-#endif
-}
-
-#pragma clang diagnostic pop
-
-static bool install_filter(filter const& f) {
-    struct sock_fprog prog = {
-        (unsigned short) f.size(),
-        (struct sock_filter*) &f[0],
-    };
-
-    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) < 0) {
-        PLOG(ERROR) << "SECCOMP: Could not set seccomp filter";
-        return false;
-    }
-
-    LOG(INFO) << "SECCOMP: Global filter installed";
-    return true;
-}
-
-bool set_seccomp_filter() {
-    filter f;
-
-    // Note that for mixed 64/32 bit architectures, ValidateArchitecture inserts a
-    // jump that must be changed to point to the start of the 32-bit policy
-    // 32 bit syscalls will not hit the policy between here and the call to SetJump
-#ifdef AUDIT_ARCH_NR32
-    auto offset_to_32bit_filter =
-#endif
-        ValidateArchitectureAndJumpIfNeeded(f);
-
-    // Native filter
-    ExamineSyscall(f);
-
-#ifdef __aarch64__
-    // Syscalls needed to boot Android
-    AllowSyscall(f, __NR_pivot_root);
-    AllowSyscall(f, __NR_ioprio_get);
-    AllowSyscall(f, __NR_ioprio_set);
-    AllowSyscall(f, __NR_gettid);
-    AllowSyscall(f, __NR_futex);
-    AllowSyscall(f, __NR_clone);
-    AllowSyscall(f, __NR_rt_sigreturn);
-    AllowSyscall(f, __NR_rt_tgsigqueueinfo);
-    AllowSyscall(f, __NR_add_key);
-    AllowSyscall(f, __NR_request_key);
-    AllowSyscall(f, __NR_keyctl);
-    AllowSyscall(f, __NR_restart_syscall);
-    AllowSyscall(f, __NR_getrandom);
-
-    // Needed for performance tools
-    AllowSyscall(f, __NR_perf_event_open);
-
-    // Needed for treble
-    AllowSyscall(f, __NR_finit_module);
-
-    // Needed for trusty
-    AllowSyscall(f, __NR_syncfs);
-
-    // Needed for strace
-    AllowSyscall(f, __NR_tkill);  // __NR_tkill
-
-    // Needed for kernel to restart syscalls
-    AllowSyscall(f, __NR_restart_syscall);
-
-     // arm64-only filter - autogenerated from bionic syscall usage
-    for (size_t i = 0; i < arm64_filter_size; ++i)
-        f.push_back(arm64_filter[i]);
-#else
-    // Generic policy
-    Allow(f);
-#endif
-
-#ifdef AUDIT_ARCH_NR32
-    if (SetValidateArchitectureJumpTarget(offset_to_32bit_filter, f) != 0)
-        return -1;
-
-    // 32-bit filter for 64-bit platforms
-    ExamineSyscall(f);
-
-#ifdef __aarch64__
-    // Syscalls needed to boot android
-    AllowSyscall(f, 120); // __NR_clone
-    AllowSyscall(f, 240); // __NR_futex
-    AllowSyscall(f, 119); // __NR_sigreturn
-    AllowSyscall(f, 173); // __NR_rt_sigreturn
-    AllowSyscall(f, 363); // __NR_rt_tgsigqueueinfo
-    AllowSyscall(f, 224); // __NR_gettid
-
-    // Syscalls needed to run Chrome
-    AllowSyscall(f, 383); // __NR_seccomp - needed to start Chrome
-    AllowSyscall(f, 384); // __NR_getrandom - needed to start Chrome
-
-    // Syscalls needed to run GFXBenchmark
-    AllowSyscall(f, 190); // __NR_vfork
-
-    // Needed for strace
-    AllowSyscall(f, 238);  // __NR_tkill
-
-    // Needed for kernel to restart syscalls
-    AllowSyscall(f, 0);  // __NR_restart_syscall
-
-    // Needed for debugging 32-bit Chrome
-    AllowSyscall(f, 42); // __NR_pipe
-
-    // b/34732712
-    AllowSyscall(f, 364); // __NR_perf_event_open
-
-    // arm32-on-arm64 only filter - autogenerated from bionic syscall usage
-    for (size_t i = 0; i < arm_filter_size; ++i)
-        f.push_back(arm_filter[i]);
-#else
-    // Generic policy
-    Allow(f);
-#endif
-#endif
-    return install_filter(f);
-}
diff --git a/init/service.cpp b/init/service.cpp
index 0f7f62f..e186f27 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -997,7 +997,7 @@
     }
 
     if (svc->Reap()) {
-        waiting_for_exec = false;
+        stop_waiting_for_exec();
         RemoveService(*svc);
     }
 
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/libappfuse/tests/FuseBufferTest.cc b/libappfuse/tests/FuseBufferTest.cc
index db35d33..1a1abd5 100644
--- a/libappfuse/tests/FuseBufferTest.cc
+++ b/libappfuse/tests/FuseBufferTest.cc
@@ -31,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);
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
index 5e54328..53ea796 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>
@@ -282,39 +283,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) {
@@ -323,7 +299,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();
@@ -345,7 +320,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;
@@ -359,6 +333,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;
 }
 
@@ -560,7 +568,7 @@
   }
   DebugFrameInfo* debug_frame = ReadDebugFrameFromFile(filename);
   if (cache_file_) {
-      g_debug_frames.emplace(filename, std::unique_ptr<DebugFrameInfo>(debug_frame));
+    g_debug_frames.emplace(filename, std::unique_ptr<DebugFrameInfo>(debug_frame));
   }
   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/libcutils/Android.bp b/libcutils/Android.bp
index cf266dc..8ba7452 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -103,6 +103,9 @@
                 "trace-dev.c",
                 "uevent.c",
             ],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
         },
 
         android_arm: {
@@ -153,9 +156,6 @@
     ],
 
     clang: true,
-    sanitize: {
-        misc_undefined: ["integer"],
-    },
 }
 
 subdirs = ["tests"]
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 013999a..b701bba 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -177,11 +177,8 @@
                                            CAP_MASK_LONG(CAP_SETPCAP),
                                               "system/bin/webview_zygote64" },
 
-    { 00755, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SYS_PTRACE),
-                                              "system/bin/crash_dump32" },
-    { 00755, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SYS_PTRACE),
-                                              "system/bin/crash_dump64" },
-
+    { 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" },
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/uncrypt" },
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/install-recovery.sh" },
diff --git a/libcutils/include/cutils/properties.h b/libcutils/include/cutils/properties.h
index adf670b..b45f58f 100644
--- a/libcutils/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/liblog/Android.bp b/liblog/Android.bp
index dce316d..310dbf4 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -15,13 +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",
-    "logger_lock.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",
@@ -29,15 +33,12 @@
 ]
 liblog_target_sources = [
     "event_tag_map.cpp",
-    "config_read.c",
     "log_time.cpp",
     "properties.c",
-    "logprint.c",
     "pmsg_reader.c",
     "pmsg_writer.c",
     "logd_reader.c",
     "logd_writer.c",
-    "logger_read.c",
 ]
 
 // Shared and static library for host and device
@@ -97,19 +98,11 @@
     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",
 }
 
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 c53ea2c..1f08eb4 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -19,6 +19,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -26,15 +27,63 @@
 
 #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"
 
-typedef std::experimental::string_view MapString;
+class MapString {
+private:
+    const std::string* alloc; // HAS-AN
+    const std::experimental::string_view str; // HAS-A
+
+public:
+    operator const std::experimental::string_view() const { return str; }
+
+    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; }
+};
+
+// 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));
+    }
+};
 
 typedef std::pair<MapString, MapString> TagFmt;
 
@@ -53,57 +102,125 @@
 
 // Map
 struct EventTagMap {
+#   define NUM_MAPS 2
     // memory-mapped source file; we get strings from here
-    void*  mapAddr;
-    size_t mapLen;
+    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() : mapAddr(NULL), mapLen(0) { }
+    EventTagMap() {
+        memset(mapAddr, 0, sizeof(mapAddr));
+        memset(mapLen, 0, sizeof(mapLen));
+    }
 
     ~EventTagMap() {
         Idx2TagFmt.clear();
-        if (mapAddr) {
-            munmap(mapAddr, mapLen);
-            mapAddr = 0;
+        TagFmt2Idx.clear();
+        Tag2Idx.clear();
+        for (size_t which = 0; which < NUM_MAPS; ++which) {
+            if (mapAddr[which]) {
+                munmap(mapAddr[which], mapLen[which]);
+                mapAddr[which] = 0;
+            }
         }
     }
 
     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;
 };
 
 bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose) {
-    std::unordered_map<uint32_t, TagFmt>::const_iterator it;
-    it = Idx2TagFmt.find(tag);
-    if (it != Idx2TagFmt.end()) {
-        if (verbose) {
-            fprintf(stderr,
-                    OUT_TAG ": duplicate tag entries %" PRIu32
-                        ":%.*s:%.*s and %" PRIu32 ":%.*s:%.*s)\n",
-                    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());
+    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 {
+            Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
         }
-        return false;
     }
 
-    Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
-    return true;
+    {
+        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));
+        }
+    }
+
+    {
+        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;
 }
 
 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
@@ -157,6 +274,19 @@
         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);
@@ -164,24 +294,33 @@
     *pData = cp;
 
     if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
-            MapString(tag, tagLen), MapString(fmt, fmtLen))), true)) {
+            MapString(tag, tagLen), MapString(fmt, fmtLen))), verbose)) {
         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) {
-    char* cp = static_cast<char*>(map->mapAddr);
-    size_t len = map->mapLen;
+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 missing EOL on last line\n");
+        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;
     }
@@ -199,7 +338,9 @@
             } else if (isdigit(*cp)) {
                 // looks like a tag; scan it out
                 if (scanTagLine(map, &cp, lineNum) != 0) {
-                    return -1;
+                    if (!which || (errno != EMLINK)) {
+                        return -1;
+                    }
                 }
                 lineNum++;      // we eat the '\n'
                 // leave lineStart==true
@@ -226,57 +367,87 @@
 // 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) {
-    int save_errno;
+    EventTagMap* newTagMap;
+    off_t end[NUM_MAPS];
+    int save_errno, fd[NUM_MAPS];
+    size_t which;
 
-    const char* tagfile = fileName ? fileName : EVENT_TAG_MAP_FILE;
-    int fd = open(tagfile, O_RDONLY | O_CLOEXEC);
-    if (fd < 0) {
+    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;
-        fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n",
-                tagfile, strerror(save_errno));
-        errno = save_errno;
-        return NULL;
-    }
-    off_t end = lseek(fd, 0L, SEEK_END);
-    save_errno = errno;
-    (void)lseek(fd, 0L, SEEK_SET);
-    if (end < 0) {
-        fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n",
-                tagfile, strerror(save_errno));
-        close(fd);
-        errno = save_errno;
-        return NULL;
+        (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
     }
 
-    EventTagMap* newTagMap = new EventTagMap;
+    newTagMap = new EventTagMap;
     if (newTagMap == NULL) {
         save_errno = errno;
-        close(fd);
-        errno = save_errno;
-        return NULL;
+        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, OUT_TAG ": mmap(%s) failed: %s\n",
-                tagfile, strerror(save_errno));
-        delete newTagMap;
-        errno = save_errno;
-        return NULL;
+    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;
+            }
+        }
     }
 
-    newTagMap->mapLen = end;
-
-    if (parseMapLines(newTagMap) != 0) {
-        delete newTagMap;
-        return NULL;
+    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.
@@ -320,3 +491,75 @@
     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/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
index 22e62ec..e57e47b 100644
--- a/liblog/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_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_radio.h b/liblog/include/log/log_radio.h
index 30a73f2..430e522 100644
--- a/liblog/include/log/log_radio.h
+++ b/liblog/include/log/log_radio.h
@@ -38,6 +38,10 @@
 
 /* --------------------------------------------------------------------- */
 
+#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.
  */
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index 5b5eebc..6a44b56 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -251,7 +251,11 @@
 #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 */
diff --git a/liblog/include/log/log_system.h b/liblog/include/log/log_system.h
index 8c1ec96..394a106 100644
--- a/liblog/include/log/log_system.h
+++ b/liblog/include/log/log_system.h
@@ -36,6 +36,10 @@
 #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.
  */
diff --git a/liblog/include/log/logprint.h b/liblog/include/log/logprint.h
index 3509e7f..5b99c3c 100644
--- a/liblog/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/liblog/legacy-ndk-includes/log.h b/liblog/legacy-ndk-includes/log.h
deleted file mode 100644
index d40d6fa..0000000
--- a/liblog/legacy-ndk-includes/log.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_LOG_H
-#define _ANDROID_LOG_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>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Android log priority values, in ascending priority order.
- */
-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;
-
-/*
- * Send a simple string to the log.
- */
-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, ...)
-#if defined(__GNUC__)
-    __attribute__((__format__(printf, 3, 4)))
-#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);
-
-/*
- * 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.
- */
-void __android_log_assert(const char *cond, const char *tag,
-			  const char *fmt, ...)    
-#if defined(__GNUC__)
-    __attribute__((__noreturn__))
-    __attribute__((__format__(printf, 3, 4)))
-#endif
-    ;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ANDROID_LOG_H */
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/logd_reader.c b/liblog/logd_reader.c
index 99d7fea..9411f36 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 || logId == LOG_ID_KERNEL) {
         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/init/seccomp.h b/liblog/logd_reader.h
similarity index 71%
copy from init/seccomp.h
copy to liblog/logd_reader.h
index cda7a89..04c2cf2 100644
--- a/init/seccomp.h
+++ b/liblog/logd_reader.h
@@ -14,9 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef SECCOMP_H
-#define SECCOMP_H
+#ifndef _LIBLOG_LOGD_READER_H__
+#define _LIBLOG_LOGD_READER_H__
 
-bool set_seccomp_filter();
+#include <unistd.h>
 
-#endif
+#include "log_portability.h"
+
+__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/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 1a2d506..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
@@ -343,7 +347,7 @@
             }
         }
         /* tag must be nul terminated */
-        if (strnlen(tag, len) >= len) {
+        if (tag && strnlen(tag, len) >= len) {
             tag = NULL;
         }
 
@@ -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..e61850d 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -16,13 +16,20 @@
 */
 
 #define _GNU_SOURCE /* for asprintf */
+#ifndef __MINGW32__
+#define HAVE_STRSEP
+#endif
 
-#include <arpa/inet.h>
+//#ifndef __MINGW32__
+//#include <arpa/inet.h>
+//#endif
 #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 +47,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 +63,7 @@
     AndroidLogPrintFormat format;
     bool colored_output;
     bool usec_time_output;
+    bool nsec_time_output;
     bool printable_output;
     bool year_output;
     bool zone_output;
@@ -210,11 +222,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 +275,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 +329,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 +337,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 +369,7 @@
         }
         free(cp);
     }
+#endif
 
     return format;
 }
@@ -405,7 +428,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 +450,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 +482,6 @@
  * Assumes single threaded execution
  *
  */
-
 LIBLOG_ABI_PUBLIC int android_log_addFilterString(
         AndroidLogFormat *p_format,
         const char *filterString)
@@ -722,6 +765,7 @@
             }
         }
     }
+    outCount = 0;
     lval = 0;
     switch (type) {
     case EVENT_TYPE_INT:
@@ -947,7 +991,7 @@
 LIBLOG_ABI_PUBLIC int android_log_processBinaryLogBuffer(
         struct logger_entry *buf,
         AndroidLogEntry *entry,
-        const EventTagMap *map,
+        const EventTagMap *map __unused,
         char *messageBuf, int messageBufLen)
 {
     size_t inCount;
@@ -989,11 +1033,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 +1063,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 */
@@ -1244,6 +1291,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 +1524,7 @@
     result->tv_nsec = entry->tv_nsec;
     subTimespec(result, result, &convert);
 }
+#endif
 
 /**
  * Formats a log message into a buffer
@@ -1496,7 +1545,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,6 +1572,7 @@
      */
     now = entry->tv_sec;
     nsec = entry->tv_nsec;
+#if __ANDROID__
     if (p_format->monotonic_output) {
         // prevent convertMonotonic from being called if logd is monotonic
         if (android_log_clockid() != CLOCK_MONOTONIC) {
@@ -1531,6 +1582,7 @@
             nsec = time.tv_nsec;
         }
     }
+#endif
     if (now < 0) {
         nsec = NS_PER_SEC - nsec;
     }
@@ -1550,7 +1602,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,13 +1636,18 @@
              * 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 {
+            } else
+#endif
+            {
                  // Not worth parsing package list, names all longer than 5
                  snprintf(uid, sizeof(uid), "%5d:", entry->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/tests/Android.mk b/liblog/tests/Android.mk
index 097befc..cfea452 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -55,7 +55,8 @@
     -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 \
@@ -111,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 8b9f0f0..bc0ea4c 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -37,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
@@ -56,7 +79,9 @@
     _rc; })
 
 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,
@@ -71,13 +96,10 @@
                                       EVENT_TYPE_STRING,
                                       Buf, sizeof(Buf) - 1));
     usleep(1000);
-#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");
@@ -141,11 +163,11 @@
     return false;
 }
 
-bool tested__android_log_close;
+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();
@@ -153,9 +175,10 @@
     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)));
+#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) {
@@ -174,15 +197,18 @@
         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)));
+#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;
@@ -225,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();
@@ -241,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;
@@ -307,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);
@@ -345,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();
@@ -354,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));
@@ -402,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);
@@ -430,7 +548,8 @@
     buf_write_test("\n Hello World \n");
 }
 
-#ifdef __ANDROID__
+#ifndef USING_LOGGER_LOCAL // requires blocking reader functionality
+#ifdef TEST_PREFIX
 static unsigned signaled;
 static log_time signal_time;
 
@@ -492,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;
 
@@ -584,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
@@ -618,7 +738,7 @@
     return NULL;
 }
 
-int start_thread()
+static int start_thread()
 {
     sem_init(&thread_trigger, 0, 0);
 
@@ -650,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;
 
@@ -742,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)
@@ -880,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));
@@ -944,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();
@@ -952,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));
@@ -1004,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)];
@@ -1058,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";
@@ -1065,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);
     }
@@ -1108,22 +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
 }
 
-#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();
@@ -1185,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";
@@ -1488,8 +1632,13 @@
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
+#endif // !USING_LOGGER_LOCAL
 
-#ifdef __ANDROID__
+// 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);
@@ -1531,10 +1680,11 @@
         testing::AssertionSuccess() :
         (testing::AssertionFailure() << message);
 }
-#endif
+#endif // TEST_PREFIX
 
 TEST(liblog, enoent) {
-#ifdef __ANDROID__
+#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));
@@ -1588,9 +1738,12 @@
     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__
     static const char persist_key[] = "persist.logd.security";
@@ -1776,15 +1929,19 @@
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
+#endif // !USING_LOGGER_LOCAL
 
-#ifdef __ANDROID__
+#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)));
 
@@ -1798,8 +1955,6 @@
 
     sleep(2);
 
-    count = 0;
-
     for (;;) {
         log_msg log_msg;
         if (android_logger_list_read(logger_list, &log_msg) <= 0) {
@@ -1839,7 +1994,7 @@
         ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
         eventData++;
 
-        int subtag_len = strlen(SUBTAG);
+        unsigned subtag_len = strlen(SUBTAG);
         if (subtag_len > 32) subtag_len = 32;
         ASSERT_EQ(subtag_len, get4LE(eventData));
         eventData += 4;
@@ -1853,7 +2008,7 @@
         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
@@ -1883,7 +2038,7 @@
 #endif
 
 TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__typical) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
     int count;
     android_errorWriteWithInfoLog_helper(
             123456781,
@@ -1899,7 +2054,7 @@
 }
 
 TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__data_too_large) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
     int count;
     android_errorWriteWithInfoLog_helper(
             123456782,
@@ -1915,7 +2070,7 @@
 }
 
 TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__null_data) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
     int count;
     android_errorWriteWithInfoLog_helper(
             123456783,
@@ -1931,7 +2086,7 @@
 }
 
 TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__subtag_too_long) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
     int count;
     android_errorWriteWithInfoLog_helper(
             123456784,
@@ -1954,12 +2109,15 @@
     buf_write_test(max_payload_buf);
 }
 
-#ifdef __ANDROID__
+#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)));
 
@@ -1972,8 +2130,6 @@
 
     sleep(2);
 
-    count = 0;
-
     for (;;) {
         log_msg log_msg;
         if (android_logger_list_read(logger_list, &log_msg) <= 0) {
@@ -2011,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))) {
@@ -2025,7 +2181,7 @@
 #endif
 
 TEST(liblog, android_errorWriteLog__android_logger_list_read__success) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
     int count;
     android_errorWriteLog_helper(123456785, "test-subtag", count);
     EXPECT_EQ(1, count);
@@ -2035,7 +2191,7 @@
 }
 
 TEST(liblog, android_errorWriteLog__android_logger_list_read__null_subtag) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
     int count;
     android_errorWriteLog_helper(123456786, NULL, count);
     EXPECT_EQ(0, count);
@@ -2044,7 +2200,8 @@
 #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) ||
@@ -2052,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;
@@ -2203,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;
 
@@ -2459,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();
@@ -2466,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);
@@ -2507,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));
@@ -2541,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";
@@ -2549,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";
@@ -2557,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";
@@ -2565,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";
@@ -2573,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";
@@ -2581,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";
@@ -2589,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";
@@ -2597,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";
@@ -2605,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";
@@ -2613,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)));
@@ -2646,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);
@@ -2663,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";
@@ -2730,8 +2894,8 @@
 }
 
 #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);
@@ -2793,7 +2957,9 @@
     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__
 // must be: '<needle:> 0 kB'
 static bool isZero(const std::string &content, std::string::size_type pos,
@@ -2876,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__
@@ -2897,7 +3063,9 @@
     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;
 
@@ -2929,3 +3097,22 @@
     }
     // 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
index 3241534..b8223f1 100644
--- a/liblog/tests/log_id_test.cpp
+++ b/liblog/tests/log_id_test.cpp
@@ -31,7 +31,6 @@
 #endif
 
 TEST(liblog, log_id) {
-#ifdef __ANDROID__
     int count = 0;
 
     for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
@@ -44,13 +43,9 @@
         fprintf(stderr, "log buffer %s\r", name);
     }
     ASSERT_EQ(LOG_ID_MAX, count);
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
 }
 
 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"));
@@ -63,13 +58,9 @@
                                          "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"));
@@ -82,26 +73,20 @@
                                          "TEST__android_log_buf_write",
                                          "main"));
     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++) {
@@ -119,7 +104,4 @@
         }
     }
     ASSERT_LT(0, ret);
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
 }
diff --git a/liblog/tests/log_radio_test.cpp b/liblog/tests/log_radio_test.cpp
index 591748a..ecba777 100644
--- a/liblog/tests/log_radio_test.cpp
+++ b/liblog/tests/log_radio_test.cpp
@@ -27,7 +27,6 @@
 #include <log/log_radio.h>
 
 TEST(liblog, RLOG) {
-#ifdef __ANDROID__
     static const char content[] = "log_radio.h";
     static const char content_false[] = "log_radio.h false";
 
@@ -84,6 +83,7 @@
     usleep(100000);
     RLOGE_IF(false, content_false);
 
+#ifdef __ANDROID__
     // give time for content to long-path through logger
     sleep(1);
 
@@ -112,6 +112,6 @@
 #endif
 
 #else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
+    GTEST_LOG_(INFO) << "This test does not test end-to-end.\n";
 #endif
 }
diff --git a/liblog/tests/log_system_test.cpp b/liblog/tests/log_system_test.cpp
index b62832e..40e3a63 100644
--- a/liblog/tests/log_system_test.cpp
+++ b/liblog/tests/log_system_test.cpp
@@ -27,7 +27,6 @@
 #include <log/log_system.h>
 
 TEST(liblog, SLOG) {
-#ifdef __ANDROID__
     static const char content[] = "log_system.h";
     static const char content_false[] = "log_system.h false";
 
@@ -84,6 +83,7 @@
     usleep(100000);
     SLOGE_IF(false, content_false);
 
+#ifdef __ANDROID__
     // give time for content to long-path through logger
     sleep(1);
 
@@ -112,6 +112,6 @@
 #endif
 
 #else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
+    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
index f2601b6..59655ba 100644
--- a/liblog/tests/log_time_test.cpp
+++ b/liblog/tests/log_time_test.cpp
@@ -21,8 +21,6 @@
 #include <log/log_time.h>
 
 TEST(liblog, log_time) {
-#ifdef __ANDROID__
-
 #ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
     log_time(CLOCK_MONOTONIC);
 
@@ -36,8 +34,4 @@
     EXPECT_EQ(tl, ts);
     EXPECT_GE(tl, ts);
     EXPECT_LE(tl, ts);
-
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
 }
diff --git a/libnativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
similarity index 75%
rename from libnativeloader/dlext_namespaces.h
rename to libnativeloader/include/nativeloader/dlext_namespaces.h
index 13a44e2..73d0be8 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,23 @@
                                                             const char* permitted_when_isolated_path,
                                                             android_namespace_t* parent);
 
+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 94c46fc..74f2f1d 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -20,7 +20,7 @@
 #include <dlfcn.h>
 #ifdef __ANDROID__
 #define LOG_TAG "libnativeloader"
-#include "dlext_namespaces.h"
+#include "nativeloader/dlext_namespaces.h"
 #include "cutils/properties.h"
 #include "log/log.h"
 #endif
@@ -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/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index ff8a300..401aaee 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -536,7 +536,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++) {
@@ -549,12 +549,12 @@
         // 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.
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/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/init/seccomp.h b/libunwindstack/Log.h
similarity index 75%
rename from init/seccomp.h
rename to libunwindstack/Log.h
index cda7a89..2d01aa8 100644
--- a/init/seccomp.h
+++ b/libunwindstack/Log.h
@@ -14,9 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef SECCOMP_H
-#define SECCOMP_H
+#ifndef _LIBUNWINDSTACK_LOG_H
+#define _LIBUNWINDSTACK_LOG_H
 
-bool set_seccomp_filter();
+#include <stdint.h>
 
-#endif
+void log_to_stdout(bool enable);
+void log(uint8_t indent, const char* format, ...);
+
+#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/init/seccomp.h b/libunwindstack/tests/LogFake.h
similarity index 66%
copy from init/seccomp.h
copy to libunwindstack/tests/LogFake.h
index cda7a89..006d393 100644
--- a/init/seccomp.h
+++ b/libunwindstack/tests/LogFake.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,9 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef SECCOMP_H
-#define SECCOMP_H
+#ifndef _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
 
-bool set_seccomp_filter();
+#include <string>
 
-#endif
+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/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/logcat/logcat.cpp b/logcat/logcat.cpp
index d9935c3..4a171fd 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -298,6 +298,8 @@
                     "                  and individually flagged modifying adverbs can be added:\n"
                     "                    color descriptive epoch monotonic printable uid\n"
                     "                    usec UTC year zone\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"
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 10d9e39..081bf92 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -107,6 +107,32 @@
     EXPECT_LT(4, count);
 }
 
+// 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) {
@@ -114,35 +140,40 @@
         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;
+
+        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 = popen(
+          "logcat -v long -v year -b all -t 3 2>/dev/null",
+          "r")));
 
-    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;
+            }
         }
-    }
+        pclose(fp);
 
-    pclose(fp);
+    } while ((count < 3) && --tries && inject(3 - count));
 
     ASSERT_EQ(3, count);
 }
@@ -179,32 +210,6 @@
     return NULL;
 }
 
-// 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(50);
-        } else if (--errors <= 0) {
-            return num;
-        }
-    }
-    // NOTREACH
-    return num;
-}
-
 TEST(logcat, tz) {
 
     if (android_log_clockid() == CLOCK_MONOTONIC) {
@@ -267,6 +272,8 @@
     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];
 
@@ -310,25 +317,32 @@
     int count;
     char buffer[BIG_BUFFER];
     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 *cp;
+    char *second_timestamp = NULL;
+    char *input;
 
     int tries = 4; // in case run too soon after system start or buffer clear
 
-    // Do not be tempted to use -v usec because that increases the
-    // chances of an occasional test failure by 1000 (see below).
     do {
-        ASSERT_TRUE(NULL != (fp = popen("logcat -v long -b all -t 10 2>&1", "r")));
-
+        ASSERT_TRUE(NULL != (fp = popen("logcat"
+                                        " -v long"
+                                        " -v nsec"
+                                        " -b all"
+                                        " -t 10"
+                                        " 2>&1", "r")));
         count = 0;
 
-        while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) {
+        while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
             ++count;
             if (!first_timestamp) {
-                first_timestamp = strdup(cp);
+                first_timestamp = strdup(input);
+            } else if (!second_timestamp) {
+                second_timestamp = strdup(input);
             }
             free(last_timestamp);
-            last_timestamp = strdup(cp);
+            last_timestamp = strdup(input);
         }
         pclose(fp);
 
@@ -337,34 +351,80 @@
     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",
-             first_timestamp);
+    snprintf(buffer, sizeof(buffer), "logcat"
+                                     " -v long"
+                                     " -v nsec"
+                                     " -b all"
+                                     " -t '%s'"
+                                     " 2>&1",
+                                     first_timestamp);
     ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
 
     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);
 
+    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);
 }
@@ -747,7 +807,7 @@
     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);
@@ -769,9 +829,15 @@
     }
     char *line = NULL;
     char *last_line = NULL; // this line is allowed to stutter, one-line overlap
-    char *second_last_line = NULL;
+    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;
@@ -789,6 +855,7 @@
     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
@@ -798,6 +865,8 @@
     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);
@@ -805,6 +874,8 @@
     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;
@@ -834,13 +905,18 @@
     }
     if (count > 1) {
         char *brk = strpbrk(second_last_line, "\r\n");
-        if (!brk) {
-            brk = second_last_line + strlen(second_last_line);
-        }
-        fprintf(stderr, "\"%.*s\" occured %u times\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);
+        }
     }
     free(second_last_line);
+    free(first_line);
 
     snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
     EXPECT_FALSE(IsFalse(system(command), command));
diff --git a/logd/Android.mk b/logd/Android.mk
index 2da9782..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,12 +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..74e0ea5 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,41 @@
     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;
+    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;
+        }
+    }
+
+    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/LogBuffer.cpp b/logd/LogBuffer.cpp
index 820ff64..7613c1e 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -199,15 +199,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);
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 932d55f..da63e12 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,6 +100,8 @@
 
     bool monotonic;
 
+    LogTags tags;
+
     LogBufferElement* lastLoggedElements[LOG_ID_MAX];
     LogBufferElement* droppedElements[LOG_ID_MAX];
     void log(LogBufferElement* elem);
@@ -133,6 +136,12 @@
     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);
+    }
+    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/LogStatistics.cpp b/logd/LogStatistics.cpp
index 273150e..7e0a6b7 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -452,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 7acef6d..777dc33 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -413,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()) {
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
new file mode 100644
index 0000000..a109592
--- /dev/null
+++ b/logd/LogTags.cpp
@@ -0,0 +1,884 @@
+/*
+ * 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::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..37a6d96
--- /dev/null
+++ b/logd/LogTags.h
@@ -0,0 +1,118 @@
+/*
+ * 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;
+    // 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 70f24e4..f044b27 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -39,8 +39,9 @@
 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);
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/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 5878f15..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;
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 2a6cdc8..adf583b 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -39,12 +39,8 @@
 #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);
@@ -52,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;
@@ -74,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;
@@ -816,6 +821,44 @@
     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_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);
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 9ab2333..1e5fa50 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -402,6 +402,7 @@
     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
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 0633a68..3b64f6d 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -46,7 +46,7 @@
 /dev/tty0                 0660   root       system
 /dev/graphics/*           0660   root       graphics
 /dev/msm_hw3dm            0660   system     graphics
-/dev/input/*              0660   root       input
+/dev/input/*              0640   system     input
 /dev/eac                  0660   root       audio
 /dev/cam                  0660   root       camera
 /dev/pmem                 0660   system     graphics
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
index e6def6b..1fb315c 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -321,7 +321,7 @@
     char idstr[80];
     struct input_id id;
 
-    fd = open(device, O_RDWR);
+    fd = open(device, O_RDONLY);
     if(fd < 0) {
         if(print_flags & PRINT_DEVICE_ERRORS)
             fprintf(stderr, "could not open %s, %s\n", device, strerror(errno));
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
index fb5c84b..769f0f5 100644
--- a/tzdatacheck/tzdatacheck.cpp
+++ b/tzdatacheck/tzdatacheck.cpp
@@ -30,26 +30,48 @@
 
 #include "android-base/logging.h"
 
-static const char* BUNDLE_VERSION_FILENAME = "/bundle_version";
-// bundle_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 bundle format (e.g. 001.001),
+// 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 bundle corrections (e.g. 001)
+// 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 BUNDLE_VERSION_LENGTH = 13;
-// The major version of the bundle format supported by this code as a null-terminated char[].
-static const char REQUIRED_BUNDLE_VERSION[] = "001";
-static const size_t REQUIRED_BUNDLE_VERSION_LEN = sizeof(REQUIRED_BUNDLE_VERSION) - 1; // exclude \0
+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;
-// Bundle version bytes are: AAA.BBB|CCCCC - the rules version is CCCCC
-static const size_t BUNDLE_VERSION_RULES_IDX = 8;
 
+// 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;
-// The major version of the bundle format supported by this code as a null-terminated char[].
+
 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
 
@@ -57,7 +79,7 @@
 static void usage() {
     std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
             "\n"
-            "Checks whether any timezone update bundle in DATA_TZ_DIR is compatible with the\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";
@@ -108,8 +130,8 @@
     return true;
 }
 
-static bool checkValidBundleVersion(const char* buffer) {
-    // See BUNDLE_VERSION_LENGTH comments above for a description of the format.
+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;
@@ -246,13 +268,13 @@
 }
 
 /*
- * Deletes the timezone update bundle directory.
+ * Deletes the timezone update distro directory.
  */
-static void deleteUpdateBundleDir(std::string& bundleDirName) {
-    LOG(INFO) << "Removing: " << bundleDirName;
-    bool deleted = deleteDir(bundleDirName);
+static void deleteUpdateDistroDir(std::string& distroDirName) {
+    LOG(INFO) << "Removing: " << distroDirName;
+    bool deleted = deleteDir(distroDirName);
     if (!deleted) {
-        LOG(WARNING) << "Deletion of bundle dir " << bundleDirName << " was not successful";
+        LOG(WARNING) << "Deletion of distro dir " << distroDirName << " was not successful";
     }
 }
 
@@ -278,60 +300,83 @@
     const char* systemZoneInfoDir = argv[1];
     const char* dataZoneInfoDir = argv[2];
 
-    // Check the bundle directory exists. If it does not, exit quickly: nothing to do.
+    // Check the distro directory exists. If it does not, exit quickly: nothing to do.
     std::string dataCurrentDirName(dataZoneInfoDir);
     dataCurrentDirName += "/current";
     int dataCurrentDirStatus = checkPath(dataCurrentDirName);
     if (dataCurrentDirStatus == NONE) {
-        LOG(INFO) << "timezone bundle dir " << dataCurrentDirName
+        LOG(INFO) << "timezone distro dir " << dataCurrentDirName
                 << " does not exist. No action required.";
         return 0;
     }
-    // If the bundle directory path is not a directory or we can't stat() the path, exit with a
+    // 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 bundle dir " << dataCurrentDirName
+        LOG(WARNING) << "Current distro dir " << dataCurrentDirName
                 << " could not be accessed or is not a directory. result=" << dataCurrentDirStatus;
         return 2;
     }
 
-    // Check the installed bundle version.
-    std::string bundleVersionFileName(dataCurrentDirName);
-    bundleVersionFileName += BUNDLE_VERSION_FILENAME;
-    std::vector<char> bundleVersion;
-    bundleVersion.reserve(BUNDLE_VERSION_LENGTH);
-    bool bundleVersionReadOk =
-            readBytes(bundleVersionFileName, bundleVersion.data(), BUNDLE_VERSION_LENGTH);
-    if (!bundleVersionReadOk) {
-        LOG(WARNING) << "bundle version file " << bundleVersionFileName
-                << " does not exist or is too short. Deleting bundle dir.";
+    // 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);
-        deleteUpdateBundleDir(dataCurrentDirName);
+        deleteUpdateDistroDir(dataCurrentDirName);
         return 3;
     }
 
-    if (!checkValidBundleVersion(bundleVersion.data())) {
-        LOG(WARNING) << "bundle version file " << bundleVersionFileName
-                << " is not valid. Deleting bundle dir.";
+    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);
-        deleteUpdateBundleDir(dataCurrentDirName);
+        deleteUpdateDistroDir(dataCurrentDirName);
         return 4;
     }
 
-    // Check the first 3 bytes of the bundleVersionHeader: these are the major version (e.g. 001).
-    // It must match exactly to be ok. The minor version is currently ignored.
-    if (strncmp(&bundleVersion[0], REQUIRED_BUNDLE_VERSION, REQUIRED_BUNDLE_VERSION_LEN) != 0) {
-        LOG(INFO) << "bundle version file " << bundleVersionFileName
-                << " is not the required version " << REQUIRED_BUNDLE_VERSION
-                << ". Deleting bundle dir.";
-        // This shouldn't happen with 001, but it in future, this will imply there has been an OTA
-        // and the installed bundle is not compatible with the new version of Android. Remove the
-        // installed bundle.
+    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);
-        deleteUpdateBundleDir(dataCurrentDirName);
+        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;
     }
 
@@ -353,22 +398,22 @@
         return 7;
     }
 
-    // Compare the bundle rules version against the system rules version.
+    // Compare the distro rules version against the system rules version.
     if (strncmp(
             &systemTzDataHeader[TZ_DATA_HEADER_PREFIX_LEN],
-            &bundleVersion[BUNDLE_VERSION_RULES_IDX],
+            &distroVersion[DISTRO_VERSION_RULES_IDX],
             RULES_VERSION_LEN) <= 0) {
-        LOG(INFO) << "Found an installed bundle but it is valid. No action taken.";
+        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 bundle.
-    LOG(INFO) << "timezone bundle in " << dataCurrentDirName << " is older than data in "
+    // 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);
-    deleteUpdateBundleDir(dataCurrentDirName);
+    deleteUpdateDistroDir(dataCurrentDirName);
     return 0;
 }