Merge "init: minor fix to READEME.md"
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 6d77f4b..c08b922 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -64,7 +64,7 @@
 
 #define FUNCTIONFS_ENDPOINT_ALLOC       _IOR('g', 231, __u32)
 
-static constexpr size_t ENDPOINT_ALLOC_RETRIES = 10;
+static constexpr size_t ENDPOINT_ALLOC_RETRIES = 2;
 
 static int dummy_fd = -1;
 
@@ -319,7 +319,7 @@
     D("[ adb: cannot call endpoint alloc: errno=%d ]", errno);
     // Kernel pre-allocation could have failed for recoverable reasons.
     // Continue running with a safe max rw size.
-    h->max_rw *= 2;
+    h->max_rw = USB_FFS_BULK_SIZE;
     return true;
 
 err:
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index 4975fab..d4f334b 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -320,6 +320,10 @@
         parent_error_sfd.reset(-1);
         close_on_exec(child_error_sfd);
 
+        // adbd sets SIGPIPE to SIG_IGN to get EPIPE instead, and Linux propagates that to child
+        // processes, so we need to manually reset back to SIG_DFL here (http://b/35209888).
+        signal(SIGPIPE, SIG_DFL);
+
         if (command_.empty()) {
             execle(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr, cenv.data());
         } else {
diff --git a/adf/libadf/adf.c b/adf/libadf/adf.c
index c4d6681..10f88b0 100644
--- a/adf/libadf/adf.c
+++ b/adf/libadf/adf.c
@@ -180,6 +180,37 @@
     return (int)data.complete_fence;
 }
 
+int adf_device_post_v2(struct adf_device *dev,
+        adf_id_t *interfaces, __u32 n_interfaces,
+        struct adf_buffer_config *bufs, __u32 n_bufs,
+        void *custom_data, __u64 custom_data_size,
+        enum adf_complete_fence_type complete_fence_type,
+        int *complete_fence)
+{
+    int err;
+    struct adf_post_config_v2 data;
+
+    memset(&data, 0, sizeof(data));
+    data.interfaces = (uintptr_t)interfaces;
+    data.n_interfaces = n_interfaces;
+    data.bufs = (uintptr_t)bufs;
+    data.n_bufs = n_bufs;
+    data.custom_data = (uintptr_t)custom_data;
+    data.custom_data_size = custom_data_size;
+    data.complete_fence_type = complete_fence_type;
+
+    err = ioctl(dev->fd, ADF_POST_CONFIG_V2, &data);
+    if (err < 0)
+        return -errno;
+
+    if (complete_fence)
+        *complete_fence = data.complete_fence;
+    else if (data.complete_fence >= 0)
+        close(data.complete_fence);
+
+    return 0;
+}
+
 static int adf_device_attachment(struct adf_device *dev,
         adf_id_t overlay_engine, adf_id_t interface, bool attach)
 {
@@ -421,6 +452,21 @@
     return (int)data.fd;
 }
 
+static void adf_interface_simple_post_config_buf(struct adf_buffer_config *buf,
+        __u32 overlay_engine, __u32 w, __u32 h, __u32 format, int buf_fd,
+        __u32 offset, __u32 pitch, int acquire_fence)
+{
+    buf->overlay_engine = overlay_engine;
+    buf->w = w;
+    buf->h = h;
+    buf->format = format;
+    buf->fd[0] = buf_fd;
+    buf->offset[0] = offset;
+    buf->pitch[0] = pitch;
+    buf->n_planes = 1;
+    buf->acquire_fence = acquire_fence;
+}
+
 int adf_interface_simple_post(int fd, __u32 overlay_engine,
         __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
         __u32 pitch, int acquire_fence)
@@ -429,16 +475,8 @@
     struct adf_simple_post_config data;
 
     memset(&data, 0, sizeof(data));
-    data.buf.overlay_engine = overlay_engine;
-    data.buf.w = w;
-    data.buf.h = h;
-    data.buf.format = format;
-    data.buf.fd[0] = buf_fd;
-    data.buf.offset[0] = offset;
-    data.buf.pitch[0] = pitch;
-    data.buf.n_planes = 1;
-    data.buf.acquire_fence = acquire_fence;
-
+    adf_interface_simple_post_config_buf(&data.buf, overlay_engine, w, h, format,
+            buf_fd, offset, pitch, acquire_fence);
     ret = ioctl(fd, ADF_SIMPLE_POST_CONFIG, &data);
     if (ret < 0)
         return -errno;
@@ -446,6 +484,32 @@
     return (int)data.complete_fence;
 }
 
+int adf_interface_simple_post_v2(int fd, adf_id_t overlay_engine,
+        __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
+        __u32 pitch, int acquire_fence,
+        enum adf_complete_fence_type complete_fence_type,
+        int *complete_fence)
+{
+    int ret;
+    struct adf_simple_post_config_v2 data;
+
+    memset(&data, 0, sizeof(data));
+    adf_interface_simple_post_config_buf(&data.buf, overlay_engine, w, h, format,
+            buf_fd, offset, pitch, acquire_fence);
+    data.complete_fence_type = complete_fence_type;
+
+    ret = ioctl(fd, ADF_SIMPLE_POST_CONFIG_V2, &data);
+    if (ret < 0)
+        return -errno;
+
+    if (complete_fence)
+        *complete_fence = data.complete_fence;
+    else if (data.complete_fence >= 0)
+        close(data.complete_fence);
+
+    return 0;
+}
+
 ssize_t adf_overlay_engines(struct adf_device *dev, adf_id_t **overlay_engines)
 {
     char pattern[64];
diff --git a/adf/libadf/include/adf/adf.h b/adf/libadf/include/adf/adf.h
index b6bda34..e4c7b28 100644
--- a/adf/libadf/include/adf/adf.h
+++ b/adf/libadf/include/adf/adf.h
@@ -75,6 +75,29 @@
         struct adf_buffer_config *bufs, size_t n_bufs,
         void *custom_data, size_t custom_data_size);
 /**
+ * Atomically posts a new display configuration to the specified interfaces.
+ *
+ * Compared to adf_device_post(), adf_device_post_v2():
+ *
+ *  (*) allows the client to choose the kind of sync fence returned
+ *      (through complete_fence_type)
+ *
+ *  (*) stores the returned sync fence fd in a provided buffer, so the client
+ *      can distinguish between a permission error (ret = -1) and a successful
+ *      call that returns no fence (*complete_fence = -1)
+ *
+ * On error, returns -errno.
+ *
+ * On devices without the corresponding kernel support, returns -ENOTTY.
+ */
+int adf_device_post_v2(struct adf_device *dev,
+        adf_id_t *interfaces, __u32 n_interfaces,
+        struct adf_buffer_config *bufs, __u32 n_bufs,
+        void *custom_data, __u64 custom_data_size,
+        enum adf_complete_fence_type complete_fence_type,
+        int *complete_fence);
+
+/**
  * Attaches the specified interface and overlay engine.
  */
 int adf_device_attach(struct adf_device *dev, adf_id_t overlay_engine,
@@ -163,6 +186,28 @@
 int adf_interface_simple_post(int fd, adf_id_t overlay_engine,
         __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
         __u32 pitch, int acquire_fence);
+/**
+ * Posts a single-plane RGB buffer to the display using the specified
+ * overlay engine.
+ *
+ * Compared to adf_interface_simple_post(), adf_interface_simple_post_v2():
+ *
+ *  (*) allows the client to choose the kind of sync fence returned
+ *      (through complete_fence_type)
+ *
+ *  (*) stores the returned sync fence fd in a provided buffer, so the client
+ *      can distinguish between a permission error (ret = -1) and a successful
+ *      call that returns no fence (*complete_fence = -1)
+ *
+ * On error, returns -errno.
+ *
+ * On devices without the corresponding kernel support, returns -ENOTTY.
+ */
+int adf_interface_simple_post_v2(int fd, adf_id_t overlay_engine,
+        __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
+        __u32 pitch, int acquire_fence,
+        enum adf_complete_fence_type complete_fence_type,
+        int *complete_fence);
 
 /**
  * Enumerates all overlay engines belonging to an ADF device.
diff --git a/adf/libadf/include/video/adf.h b/adf/libadf/include/video/adf.h
index 77203a5..692a425 100644
--- a/adf/libadf/include/video/adf.h
+++ b/adf/libadf/include/video/adf.h
@@ -49,69 +49,94 @@
   ADF_EVENT_DEVICE_CUSTOM = 128,
   ADF_EVENT_TYPE_MAX = 255,
 };
-struct adf_set_event {
+enum adf_complete_fence_type {
 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  ADF_COMPLETE_FENCE_NONE = 0,
+  ADF_COMPLETE_FENCE_PRESENT = 1,
+  ADF_COMPLETE_FENCE_RELEASE = 2,
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_set_event {
   __u8 type;
   __u8 enabled;
 };
-struct adf_event {
 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_event {
   __u8 type;
   __u32 length;
 };
-struct adf_vsync_event {
 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_vsync_event {
   struct adf_event base;
   __aligned_u64 timestamp;
 };
-struct adf_hotplug_event {
 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_hotplug_event {
   struct adf_event base;
   __u8 connected;
 };
-#define ADF_MAX_PLANES 4
 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_MAX_PLANES 4
 struct adf_buffer_config {
   __u32 overlay_engine;
   __u32 w;
-  __u32 h;
 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u32 h;
   __u32 format;
   __s32 fd[ADF_MAX_PLANES];
   __u32 offset[ADF_MAX_PLANES];
-  __u32 pitch[ADF_MAX_PLANES];
 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u32 pitch[ADF_MAX_PLANES];
   __u8 n_planes;
   __s32 acquire_fence;
 };
-#define ADF_MAX_BUFFERS (4096 / sizeof(struct adf_buffer_config))
 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_MAX_BUFFERS (4096 / sizeof(struct adf_buffer_config))
 struct adf_post_config {
   size_t n_interfaces;
   __u32 __user * interfaces;
-  size_t n_bufs;
 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  size_t n_bufs;
   struct adf_buffer_config __user * bufs;
   size_t custom_data_size;
   void __user * custom_data;
-  __s32 complete_fence;
 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __s32 complete_fence;
 };
+struct adf_post_config_v2 {
+  __u32 n_interfaces;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u64 interfaces;
+  __u32 n_bufs;
+  __u64 bufs;
+  __u64 custom_data_size;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u64 custom_data;
+  __s32 complete_fence;
+  __u8 complete_fence_type;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
 #define ADF_MAX_INTERFACES (4096 / sizeof(__u32))
 struct adf_simple_buffer_alloc {
   __u16 w;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
   __u16 h;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
   __u32 format;
   __s32 fd;
   __u32 offset;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
   __u32 pitch;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
 };
 struct adf_simple_post_config {
   struct adf_buffer_config buf;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
   __s32 complete_fence;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct adf_simple_post_config_v2 {
+  struct adf_buffer_config buf;
+  __s32 complete_fence;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  __u8 complete_fence_type;
 };
 struct adf_attachment_config {
   __u32 overlay_engine;
@@ -177,4 +202,8 @@
 #define ADF_ATTACH _IOW(ADF_IOCTL_TYPE, 9, struct adf_attachment_config)
 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
 #define ADF_DETACH _IOW(ADF_IOCTL_TYPE, 10, struct adf_attachment_config)
+#define ADF_POST_CONFIG_V2 _IOW(ADF_IOCTL_TYPE, 11, struct adf_post_config_v2)
+#define ADF_SIMPLE_POST_CONFIG_V2 _IOW(ADF_IOCTL_TYPE, 12, struct adf_simple_post_config_v2)
 #endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+
diff --git a/adf/libadf/original-kernel-headers/video/adf.h b/adf/libadf/original-kernel-headers/video/adf.h
index c5d2e62..8293c1d 100644
--- a/adf/libadf/original-kernel-headers/video/adf.h
+++ b/adf/libadf/original-kernel-headers/video/adf.h
@@ -46,6 +46,15 @@
 	ADF_EVENT_TYPE_MAX = 255,
 };
 
+enum adf_complete_fence_type {
+	/* no fence */
+	ADF_COMPLETE_FENCE_NONE = 0,
+	/* fence fires when the configuration appears on the screen */
+	ADF_COMPLETE_FENCE_PRESENT = 1,
+	/* fence fires when the configuration leaves the screen */
+	ADF_COMPLETE_FENCE_RELEASE = 2,
+};
+
 /**
  * struct adf_set_event - start or stop subscribing to ADF events
  *
@@ -131,6 +140,9 @@
 /**
  * struct adf_post_config - request to flip to a new set of buffers
  *
+ * This request is equivalent to &struct adf_post_config_v2 with
+ * @complete_fence_type = %ADF_COMPLETE_FENCE_RELEASE.
+ *
  * @n_interfaces: number of interfaces targeted by the flip (input)
  * @interfaces: ids of interfaces targeted by the flip (input)
  * @n_bufs: number of buffers displayed (input)
@@ -152,6 +164,34 @@
 
 	__s32 complete_fence;
 };
+
+/**
+ * struct adf_post_config_v2 - request to flip to a new set of buffers
+ *
+ * @n_interfaces: number of interfaces targeted by the flip (input)
+ * @interfaces: ids of interfaces targeted by the flip (input)
+ * @n_bufs: number of buffers displayed (input)
+ * @bufs: description of buffers displayed (input)
+ * @custom_data_size: size of driver-private data (input)
+ * @custom_data: driver-private data (input)
+ * @complete_fence_type: one of &enum adf_complete_fence_type describing what
+ * 	fence to return (input)
+ * @complete_fence: sync_fence fd which will fire at the time
+ * 	requested by @complete_fence_type (output)
+ */
+struct adf_post_config_v2 {
+	__u32 n_interfaces;
+	__u64 interfaces; /* __u32 * packed into __u64 */
+
+	__u32 n_bufs;
+	__u64 bufs; /* struct adf_buffer_config * packed into __u64 */
+
+	__u64 custom_data_size;
+	__u64 custom_data; /* void * packed into __u64 */
+
+	__s32 complete_fence;
+	__u8 complete_fence_type;
+};
 #define ADF_MAX_INTERFACES (4096 / sizeof(__u32))
 
 /**
@@ -189,6 +229,9 @@
  * struct adf_simple_post_config - request to flip to a single buffer without
  * driver-private data
  *
+ * This request is equivalent to &struct adf_simple_post_config_v2 with
+ * @complete_fence_type = %ADF_COMPLETE_FENCE_RELEASE.
+ *
  * @buf: description of buffer displayed (input)
  * @complete_fence: sync_fence fd which will clear when this buffer has left the
  * screen (output)
@@ -199,6 +242,22 @@
 };
 
 /**
+ * struct adf_simple_post_config_v2 - request to flip to a single buffer without
+ * driver-private data
+ *
+ * @buf: description of buffer displayed (input)
+ * @complete_fence_type: one of &enum adf_complete_fence_type describing what
+ * 	fence to return (input)
+ * @complete_fence: sync_fence fd which will fire at the time
+ * 	requested by @complete_fence_type (output)
+ */
+struct adf_simple_post_config_v2 {
+	struct adf_buffer_config buf;
+	__s32 complete_fence;
+	__u8 complete_fence_type;
+};
+
+/**
  * struct adf_attachment_config - description of attachment between an overlay
  * engine and an interface
  *
@@ -318,4 +377,10 @@
 #define ADF_DETACH		_IOW(ADF_IOCTL_TYPE, 10, \
 					struct adf_attachment_config)
 
+#define ADF_POST_CONFIG_V2	_IOW(ADF_IOCTL_TYPE, 11, \
+					struct adf_post_config_v2)
+#define ADF_SIMPLE_POST_CONFIG_V2 \
+				_IOW(ADF_IOCTL_TYPE, 12, \
+					struct adf_simple_post_config_v2)
+
 #endif /* _UAPI_VIDEO_ADF_H_ */
diff --git a/adf/libadf/tests/Android.bp b/adf/libadf/tests/Android.bp
index 7b33300..9b3430e 100644
--- a/adf/libadf/tests/Android.bp
+++ b/adf/libadf/tests/Android.bp
@@ -17,6 +17,7 @@
 cc_test {
     name: "adf-unit-tests",
     srcs: ["adf_test.cpp"],
+    shared_libs: ["libsync"],
     static_libs: ["libadf"],
     cflags: ["-Werror"],
 }
diff --git a/adf/libadf/tests/adf_test.cpp b/adf/libadf/tests/adf_test.cpp
index eaa9342..4727c2b 100644
--- a/adf/libadf/tests/adf_test.cpp
+++ b/adf/libadf/tests/adf_test.cpp
@@ -20,6 +20,7 @@
 #include <adf/adf.h>
 #include <gtest/gtest.h>
 #include <sys/mman.h>
+#include <sync/sync.h>
 
 class AdfTest : public testing::Test {
 public:
@@ -73,24 +74,6 @@
         FAIL(); /* this should never happen */
     }
 
-    void drawCheckerboard(void *buf, uint32_t w, uint32_t h, uint32_t pitch) {
-        uint8_t *buf8 = reinterpret_cast<uint8_t *>(buf);
-        for (uint32_t y = 0; y < h / 2; y++) {
-            uint32_t *scanline = reinterpret_cast<uint32_t *>(buf8 + y * pitch);
-            for (uint32_t x = 0; x < w / 2; x++)
-                scanline[x] = 0xFF0000FF;
-            for (uint32_t x = w / 2; x < w; x++)
-                scanline[x] = 0xFF00FFFF;
-        }
-        for (uint32_t y = h / 2; y < h; y++) {
-            uint32_t *scanline = reinterpret_cast<uint32_t *>(buf8 + y * pitch);
-            for (uint32_t x = 0; x < w / 2; x++)
-                scanline[x] = 0xFFFF00FF;
-            for (uint32_t x = w / 2; x < w; x++)
-                scanline[x] = 0xFFFFFFFF;
-        }
-    }
-
     /* various helpers to call ADF and die on failure */
 
     void getInterfaceData(adf_interface_data &data) {
@@ -141,6 +124,42 @@
         free(event);
     }
 
+    void drawCheckerboard(uint32_t &w, uint32_t &h, uint32_t &format,
+            char format_str[ADF_FORMAT_STR_SIZE], int &buf_fd, uint32_t &offset,
+            uint32_t &pitch) {
+        ASSERT_NO_FATAL_FAILURE(getCurrentMode(w, h));
+        ASSERT_NO_FATAL_FAILURE(get8888Format(format, format_str));
+
+        buf_fd = adf_interface_simple_buffer_alloc(intf, w, h, format, &offset,
+                &pitch);
+        ASSERT_GE(buf_fd, 0) << "allocating " << w << "x" << h << " " <<
+                format_str << " buffer failed: " << strerror(-buf_fd);
+        EXPECT_GE(pitch, w * 4);
+
+        void *mapped = mmap(NULL, pitch * h, PROT_WRITE, MAP_SHARED, buf_fd,
+                offset);
+        ASSERT_NE(mapped, MAP_FAILED) << "mapping " << w << "x" << h << " " <<
+                format_str << " buffer failed: " << strerror(-errno);
+
+        uint8_t *buf8 = static_cast<uint8_t *>(mapped);
+        for (uint32_t y = 0; y < h / 2; y++) {
+            uint32_t *scanline = reinterpret_cast<uint32_t *>(buf8 + y * pitch);
+            for (uint32_t x = 0; x < w / 2; x++)
+                scanline[x] = 0xFF0000FF;
+            for (uint32_t x = w / 2; x < w; x++)
+                scanline[x] = 0xFF00FFFF;
+        }
+        for (uint32_t y = h / 2; y < h; y++) {
+            uint32_t *scanline = reinterpret_cast<uint32_t *>(buf8 + y * pitch);
+            for (uint32_t x = 0; x < w / 2; x++)
+                scanline[x] = 0xFFFF00FF;
+            for (uint32_t x = w / 2; x < w; x++)
+                scanline[x] = 0xFFFFFFFF;
+        }
+
+        munmap(mapped, pitch * h);
+    }
+
 protected:
     adf_device dev;
     adf_id_t intf_id;
@@ -310,27 +329,11 @@
 }
 
 TEST_F(AdfTest, simple_buffer) {
-    uint32_t w = 0, h = 0;
-    ASSERT_NO_FATAL_FAILURE(getCurrentMode(w, h));
-
-    uint32_t format = 0;
+    int buf_fd;
+    uint32_t w, h, format, offset, pitch;
     char format_str[ADF_FORMAT_STR_SIZE];
-    ASSERT_NO_FATAL_FAILURE(get8888Format(format, format_str));
-
-    uint32_t offset;
-    uint32_t pitch;
-    int buf_fd = adf_interface_simple_buffer_alloc(intf, w, h, format, &offset,
-            &pitch);
-    ASSERT_GE(buf_fd, 0) << "allocating " << w << "x" << h << " " <<
-            format_str << " buffer failed: " << strerror(-buf_fd);
-    EXPECT_GE(pitch, w * 4);
-
-    void *mapped = mmap(NULL, pitch * h, PROT_WRITE, MAP_SHARED, buf_fd,
-            offset);
-    ASSERT_NE(mapped, MAP_FAILED) << "mapping " << w << "x" << h << " " <<
-            format_str << " buffer failed: " << strerror(-errno);
-    drawCheckerboard(mapped, w, h, pitch);
-    munmap(mapped, pitch * h);
+    ASSERT_NO_FATAL_FAILURE(drawCheckerboard(w, h, format, format_str,
+            buf_fd, offset, pitch));
 
     ASSERT_NO_FATAL_FAILURE(attach());
     ASSERT_NO_FATAL_FAILURE(blank(DRM_MODE_DPMS_ON));
@@ -342,3 +345,59 @@
             format_str << " buffer failed: " << strerror(-release_fence);
     close(release_fence);
 }
+
+TEST_F(AdfTest, simple_buffer_v2) {
+    int buf_fd;
+    uint32_t w, h, format, offset, pitch;
+    char format_str[ADF_FORMAT_STR_SIZE];
+    ASSERT_NO_FATAL_FAILURE(drawCheckerboard(w, h, format, format_str,
+            buf_fd, offset, pitch));
+
+    ASSERT_NO_FATAL_FAILURE(attach());
+    ASSERT_NO_FATAL_FAILURE(blank(DRM_MODE_DPMS_ON));
+
+    int config_1_release;
+    int err = adf_interface_simple_post_v2(intf, eng_id, w, h,
+            format, buf_fd, offset, pitch, -1, ADF_COMPLETE_FENCE_RELEASE,
+            &config_1_release);
+    if (err == -ENOTTY) {
+        GTEST_LOG_(INFO) << "ADF_SIMPLE_POST_CONFIG_V2 not supported on this kernel";
+        return;
+    }
+    ASSERT_GE(err, 0) << "posting " << w << "x" << h << " " <<
+            format_str << " buffer failed: " << strerror(-err);
+
+    err = sync_wait(config_1_release, 1000);
+    ASSERT_EQ(-1, err) <<
+            "waiting for config 1's release fence should not have suceeded";
+    ASSERT_EQ(ETIME, errno) <<
+            "config 1's release fence should have timed out, but failed instead: " <<
+            strerror(errno);
+
+    int config_2_present;
+    err = adf_interface_simple_post_v2(intf, eng_id, w, h,
+            format, buf_fd, offset, pitch, -1, ADF_COMPLETE_FENCE_PRESENT,
+            &config_2_present);
+    ASSERT_GE(err, 0) << "posting " << w << "x" << h << " " <<
+            format_str << " buffer failed: " << strerror(-err);
+
+    err = sync_wait(config_2_present, 1000);
+    ASSERT_EQ(0, err) <<
+            "waiting for config 2's present fence failed: " << strerror(errno);
+    err = sync_wait(config_1_release, 0);
+    ASSERT_EQ(0, err) <<
+            "waiting for config 1's release fence failed: " << strerror(errno);
+    close(config_1_release);
+    close(config_2_present);
+
+    int config_3_no_fence;
+    err = adf_interface_simple_post_v2(intf, eng_id, w, h,
+            format, buf_fd, offset, pitch, -1, ADF_COMPLETE_FENCE_NONE,
+            &config_3_no_fence);
+    ASSERT_GE(err, 0) << "posting " << w << "x" << h << " " <<
+            format_str << " buffer failed: " << strerror(-err);
+    ASSERT_EQ(-1, config_3_no_fence) <<
+            "fence returned even though the fence type was ADF_COMPLETE_FENCE_NONE";
+
+    close(buf_fd);
+}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index a7354a7..a4cc5f2 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -225,7 +225,12 @@
 void RecordBootloaderTimings(BootEventRecordStore* boot_event_store) {
   // |ro.boot.boottime| is of the form 'stage1:time1,...,stageN:timeN'.
   std::string value = GetProperty("ro.boot.boottime");
+  if (value.empty()) {
+    // ro.boot.boottime is not reported on all devices.
+    return;
+  }
 
+  int32_t total_time = 0;
   auto stages = android::base::Split(value, ",");
   for (auto const &stageTiming : stages) {
     // |stageTiming| is of the form 'stage:time'.
@@ -235,10 +240,13 @@
     std::string stageName = stageTimingValues[0];
     int32_t time_ms;
     if (android::base::ParseInt(stageTimingValues[1], &time_ms)) {
+      total_time += time_ms;
       boot_event_store->AddBootEventWithValue(
           "boottime.bootloader." + stageName, time_ms);
     }
   }
+
+  boot_event_store->AddBootEventWithValue("boottime.bootloader.total", total_time);
 }
 
 // Records several metrics related to the time it takes to boot the device,
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index ca881aa..8d2ea68 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -12,14 +12,48 @@
 }
 
 cc_library_static {
-    name: "libdebuggerd_handler",
+    name: "libdebuggerd_handler_core",
     defaults: ["debuggerd_defaults"],
     srcs: ["handler/debuggerd_handler.cpp"],
 
     // libdebuggerd_handler gets async signal safe logging via libc_logging,
     // which defines its interface in bionic private headers.
     include_dirs: ["bionic/libc"],
-    static_libs: ["libc_logging"],
+    whole_static_libs: [
+        "libc_logging",
+        "libdebuggerd",
+    ],
+
+    export_include_dirs: ["include"],
+}
+
+cc_library_static {
+    name: "libdebuggerd_handler",
+    defaults: ["debuggerd_defaults"],
+    srcs: ["handler/debuggerd_fallback_nop.cpp"],
+
+    whole_static_libs: [
+        "libdebuggerd_handler_core",
+    ],
+
+    export_include_dirs: ["include"],
+}
+
+cc_library_static {
+    name: "libdebuggerd_handler_fallback",
+    defaults: ["debuggerd_defaults"],
+    srcs: ["handler/debuggerd_fallback.cpp"],
+
+    // libdebuggerd_handler gets async signal safe logging via libc_logging,
+    // which defines its interface in bionic private headers.
+    include_dirs: ["bionic/libc"],
+    static_libs: [
+        "libdebuggerd",
+        "libbacktrace",
+        "libunwind",
+        "liblzma",
+        "libcutils",
+    ],
 
     export_include_dirs: ["include"],
 }
@@ -39,7 +73,7 @@
     export_include_dirs: ["include"],
 }
 
-cc_library {
+cc_library_static {
     name: "libdebuggerd",
     defaults: ["debuggerd_defaults"],
 
@@ -75,8 +109,10 @@
     local_include_dirs: ["libdebuggerd/include"],
     export_include_dirs: ["libdebuggerd/include"],
 
-    shared_libs: [
+    static_libs: [
         "libbacktrace",
+        "libunwind",
+        "liblzma",
         "libbase",
         "libcutils",
         "liblog",
@@ -150,10 +186,14 @@
         },
     },
 
+    static_libs: [
+        "libdebuggerd",
+        "libcutils",
+    ],
+
     shared_libs: [
         "libbacktrace",
         "libbase",
-        "libdebuggerd",
         "liblog",
         "libprocinfo",
         "libselinux",
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 2889356..0e15472 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -155,6 +155,14 @@
     return false;
   }
 
+  // Make the fd O_APPEND so that our output is guaranteed to be at the end of a file.
+  // (This also makes selinux rules consistent, because selinux distinguishes between writing to
+  // a regular fd, and writing to an fd with O_APPEND).
+  int flags = fcntl(tmp_output_fd.get(), F_GETFL);
+  if (fcntl(tmp_output_fd.get(), F_SETFL, flags | O_APPEND) != 0) {
+    PLOG(WARNING) << "failed to set output fd flags";
+  }
+
   *tombstoned_socket = std::move(sockfd);
   *output_fd = std::move(tmp_output_fd);
   return true;
@@ -211,17 +219,6 @@
   }
 }
 
-static void check_process(int proc_fd, pid_t expected_pid) {
-  android::procinfo::ProcessInfo proc_info;
-  if (!android::procinfo::GetProcessInfoFromProcPidFd(proc_fd, &proc_info)) {
-    LOG(FATAL) << "failed to fetch process info";
-  }
-
-  if (proc_info.pid != expected_pid) {
-    LOG(FATAL) << "pid mismatch: expected " << expected_pid << ", actual " << proc_info.ppid;
-  }
-}
-
 int main(int argc, char** argv) {
   pid_t target = getppid();
   bool tombstoned_connected = false;
@@ -238,11 +235,12 @@
   action.sa_handler = signal_handler;
   debuggerd_register_handlers(&action);
 
-  if (argc != 2) {
+  if (argc != 3) {
     return 1;
   }
 
   pid_t main_tid;
+  pid_t pseudothread_tid;
 
   if (target == 1) {
     LOG(FATAL) << "target died before we could attach";
@@ -252,6 +250,10 @@
     LOG(FATAL) << "invalid main tid: " << argv[1];
   }
 
+  if (!android::base::ParseInt(argv[2], &pseudothread_tid, 1, std::numeric_limits<pid_t>::max())) {
+    LOG(FATAL) << "invalid pseudothread tid: " << argv[2];
+  }
+
   android::procinfo::ProcessInfo target_info;
   if (!android::procinfo::GetProcessInfo(main_tid, &target_info)) {
     LOG(FATAL) << "failed to fetch process info for target " << main_tid;
@@ -269,6 +271,11 @@
     PLOG(FATAL) << "failed to open " << target_proc_path;
   }
 
+  // Make sure our parent didn't die.
+  if (getppid() != target) {
+    PLOG(FATAL) << "parent died";
+  }
+
   // Reparent ourselves to init, so that the signal handler can waitpid on the
   // original process to avoid leaving a zombie for non-fatal dumps.
   pid_t forkpid = fork();
@@ -281,19 +288,53 @@
   // Die if we take too long.
   alarm(20);
 
-  check_process(target_proc_fd, target);
-
   std::string attach_error;
+
+  // Seize the main thread.
   if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
     LOG(FATAL) << attach_error;
   }
 
-  check_process(target_proc_fd, target);
+  // Seize the siblings.
+  std::set<pid_t> attached_siblings;
+  {
+    std::set<pid_t> siblings;
+    if (!android::procinfo::GetProcessTids(target, &siblings)) {
+      PLOG(FATAL) << "failed to get process siblings";
+    }
+
+    // but not the already attached main thread.
+    siblings.erase(main_tid);
+    // or the handler pseudothread.
+    siblings.erase(pseudothread_tid);
+
+    for (pid_t sibling_tid : siblings) {
+      if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) {
+        LOG(WARNING) << attach_error;
+      } else {
+        attached_siblings.insert(sibling_tid);
+      }
+    }
+  }
+
+  // Collect the backtrace map and open files, while the process still has PR_GET_DUMPABLE=1
+  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(main_tid));
+  if (!backtrace_map) {
+    LOG(FATAL) << "failed to create backtrace map";
+  }
+
+  // Collect the list of open files.
+  OpenFilesList open_files;
+  populate_open_files_list(target, &open_files);
+
+  // Drop our capabilities now that we've attached to the threads we care about.
+  drop_capabilities();
 
   LOG(INFO) << "obtaining output fd from tombstoned";
   tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd);
 
   // Write a '\1' to stdout to tell the crashing process to resume.
+  // It also restores the value of PR_SET_DUMPABLE at this point.
   if (TEMP_FAILURE_RETRY(write(STDOUT_FILENO, "\1", 1)) == -1) {
     PLOG(ERROR) << "failed to communicate to target process";
   }
@@ -339,50 +380,14 @@
     abort_address = reinterpret_cast<uintptr_t>(siginfo.si_value.sival_ptr);
   }
 
-  // Now that we have the signal that kicked things off, attach all of the
-  // sibling threads, and then proceed.
-  std::set<pid_t> attached_siblings;
-  {
-    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_proc_fd, sibling_tid, &attach_error)) {
-        LOG(WARNING) << attach_error;
-      } else {
-        attached_siblings.insert(sibling_tid);
-      }
-    }
-  }
-
-  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::string amfd_data;
-
   if (backtrace) {
     dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, attached_siblings, 0);
   } else {
-    engrave_tombstone(output_fd.get(), backtrace_map.get(), open_files, target, main_tid,
-                      attached_siblings, abort_address, fatal_signal ? &amfd_data : nullptr);
+    engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid,
+                      &attached_siblings, abort_address, fatal_signal ? &amfd_data : nullptr);
   }
 
   // We don't actually need to PTRACE_DETACH, as long as our tracees aren't in
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 64a38dd..1c01e3e 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
+#include <sys/prctl.h>
 #include <unistd.h>
 
 // We test both kinds of logging.
@@ -188,6 +189,9 @@
     fprintf(stderr, "  fprintf-NULL          pass a null pointer to fprintf\n");
     fprintf(stderr, "  readdir-NULL          pass a null pointer to readdir\n");
     fprintf(stderr, "  strlen-NULL           pass a null pointer to strlen\n");
+    fprintf(stderr, "  pthread_join-NULL     pass a null pointer to pthread_join\n");
+    fprintf(stderr, "\n");
+    fprintf(stderr, "  no_new_privs          set PR_SET_NO_NEW_PRIVS and then abort\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "prefix any of the above with 'thread-' to run on a new thread\n");
     fprintf(stderr, "prefix any of the above with 'exhaustfd-' to exhaust\n");
@@ -255,6 +259,8 @@
         readdir_null();
     } else if (!strcasecmp(arg, "strlen-NULL")) {
         return strlen_null();
+    } else if (!strcasecmp(arg, "pthread_join-NULL")) {
+        return pthread_join(0, nullptr);
     } else if (!strcasecmp(arg, "heap-usage")) {
         abuse_heap();
     } else if (!strcasecmp(arg, "SIGSEGV-unmapped")) {
@@ -276,6 +282,12 @@
     } else if (!strcasecmp(arg, "kuser_cmpxchg64")) {
         return __kuser_cmpxchg64(0, 0, 0);
 #endif
+    } else if (!strcasecmp(arg, "no_new_privs")) {
+        if (prctl(PR_SET_NO_NEW_PRIVS, 1) != 0) {
+          fprintf(stderr, "prctl(PR_SET_NO_NEW_PRIVS, 1) failed: %s\n", strerror(errno));
+          return EXIT_SUCCESS;
+        }
+        abort();
     } else {
         return usage();
     }
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index e22d6a9..1a27f3f 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -17,6 +17,7 @@
 #include <err.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <sys/capability.h>
 #include <sys/prctl.h>
 #include <sys/types.h>
 
@@ -24,6 +25,8 @@
 #include <regex>
 #include <thread>
 
+#include <android/set_abort_message.h>
+
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
@@ -40,10 +43,8 @@
 using android::base::unique_fd;
 
 #if defined(__LP64__)
-#define CRASHER_PATH  "/system/bin/crasher64"
 #define ARCH_SUFFIX "64"
 #else
-#define CRASHER_PATH "/system/bin/crasher"
 #define ARCH_SUFFIX ""
 #endif
 
@@ -179,23 +180,14 @@
   if (crasher_pid == -1) {
     FAIL() << "fork failed: " << strerror(errno);
   } else if (crasher_pid == 0) {
-    unique_fd devnull(open("/dev/null", O_WRONLY));
-    dup2(crasher_read_pipe.get(), STDIN_FILENO);
-    dup2(devnull.get(), STDOUT_FILENO);
-    dup2(devnull.get(), STDERR_FILENO);
+    char dummy;
+    crasher_pipe.reset();
+    TEMP_FAILURE_RETRY(read(crasher_read_pipe.get(), &dummy, 1));
     function();
     _exit(0);
   }
 }
 
-void CrasherTest::StartCrasher(const std::string& crash_type) {
-  std::string type = "wait-" + crash_type;
-  StartProcess([type]() {
-    execl(CRASHER_PATH, CRASHER_PATH, type.c_str(), nullptr);
-    exit(errno);
-  });
-}
-
 void CrasherTest::FinishCrasher() {
   if (crasher_pipe == -1) {
     FAIL() << "crasher pipe uninitialized";
@@ -249,7 +241,10 @@
 TEST_F(CrasherTest, smoke) {
   int intercept_result;
   unique_fd output_fd;
-  StartCrasher("SIGSEGV");
+  StartProcess([]() {
+    *reinterpret_cast<volatile char*>(0xdead) = '1';
+  });
+
   StartIntercept(&output_fd);
   FinishCrasher();
   AssertDeath(SIGSEGV);
@@ -265,7 +260,9 @@
 TEST_F(CrasherTest, abort) {
   int intercept_result;
   unique_fd output_fd;
-  StartCrasher("abort");
+  StartProcess([]() {
+    abort();
+  });
   StartIntercept(&output_fd);
   FinishCrasher();
   AssertDeath(SIGABRT);
@@ -281,7 +278,9 @@
 TEST_F(CrasherTest, signal) {
   int intercept_result;
   unique_fd output_fd;
-  StartCrasher("abort");
+  StartProcess([]() {
+    abort();
+  });
   StartIntercept(&output_fd);
 
   // Wait for a bit, or we might end up killing the process before the signal
@@ -303,7 +302,10 @@
 TEST_F(CrasherTest, abort_message) {
   int intercept_result;
   unique_fd output_fd;
-  StartCrasher("smash-stack");
+  StartProcess([]() {
+    android_set_abort_message("abort message goes here");
+    abort();
+  });
   StartIntercept(&output_fd);
   FinishCrasher();
   AssertDeath(SIGABRT);
@@ -313,13 +315,15 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(Abort message: 'stack corruption detected \(-fstack-protector\)')");
+  ASSERT_MATCH(result, R"(Abort message: 'abort message goes here')");
 }
 
 TEST_F(CrasherTest, intercept_timeout) {
   int intercept_result;
   unique_fd output_fd;
-  StartCrasher("abort");
+  StartProcess([]() {
+    abort();
+  });
   StartIntercept(&output_fd);
 
   // Don't let crasher finish until we timeout.
@@ -338,7 +342,9 @@
   }
   sleep(1);
 
-  StartCrasher("abort");
+  StartProcess([]() {
+    abort();
+  });
   FinishCrasher();
 
   int status;
@@ -357,7 +363,9 @@
     FAIL() << "failed to enable wait_for_gdb";
   }
 
-  StartCrasher("abort");
+  StartProcess([]() {
+    abort();
+  });
   ASSERT_EQ(0, kill(crasher_pid, SIGSEGV)) << strerror(errno);
   AssertDeath(SIGSEGV);
 }
@@ -366,7 +374,10 @@
   std::string result;
   int intercept_result;
   unique_fd output_fd;
-  StartCrasher("abort");
+
+  StartProcess([]() {
+    abort();
+  });
   StartIntercept(&output_fd);
 
   std::this_thread::sleep_for(500ms);
@@ -392,20 +403,78 @@
 }
 
 TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
+  int intercept_result;
+  unique_fd output_fd;
   StartProcess([]() {
     prctl(PR_SET_DUMPABLE, 0);
-    volatile char* null = static_cast<char*>(nullptr);
-    *null = '\0';
+    abort();
   });
-  AssertDeath(SIGSEGV);
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
 }
 
-TEST_F(CrasherTest, PR_SET_DUMPABLE_0_raise) {
+TEST_F(CrasherTest, capabilities) {
+  ASSERT_EQ(0U, getuid()) << "capability test requires root";
+
   StartProcess([]() {
-    prctl(PR_SET_DUMPABLE, 0);
-    raise(SIGUSR1);
+    if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0) {
+      err(1, "failed to set PR_SET_KEEPCAPS");
+    }
+
+    if (setresuid(1, 1, 1) != 0) {
+      err(1, "setresuid failed");
+    }
+
+    __user_cap_header_struct capheader;
+    __user_cap_data_struct capdata[2];
+    memset(&capheader, 0, sizeof(capheader));
+    memset(&capdata, 0, sizeof(capdata));
+
+    capheader.version = _LINUX_CAPABILITY_VERSION_3;
+    capheader.pid = 0;
+
+    // Turn on every third capability.
+    static_assert(CAP_LAST_CAP > 33, "CAP_LAST_CAP <= 32");
+    for (int i = 0; i < CAP_LAST_CAP; i += 3) {
+      capdata[CAP_TO_INDEX(i)].permitted |= CAP_TO_MASK(i);
+      capdata[CAP_TO_INDEX(i)].effective |= CAP_TO_MASK(i);
+    }
+
+    // Make sure CAP_SYS_PTRACE is off.
+    capdata[CAP_TO_INDEX(CAP_SYS_PTRACE)].permitted &= ~(CAP_TO_MASK(CAP_SYS_PTRACE));
+    capdata[CAP_TO_INDEX(CAP_SYS_PTRACE)].effective &= ~(CAP_TO_MASK(CAP_SYS_PTRACE));
+
+    if (capset(&capheader, &capdata[0]) != 0) {
+      err(1, "capset failed");
+    }
+
+    if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) != 0) {
+      err(1, "failed to drop ambient capabilities");
+    }
+
+    raise(SIGSYS);
   });
-  AssertDeath(SIGUSR1);
+
+  unique_fd output_fd;
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSYS);
+
+  std::string result;
+  int intercept_result;
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
 }
 
 TEST(crash_dump, zombie) {
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
new file mode 100644
index 0000000..77ad6ac
--- /dev/null
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stddef.h>
+#include <sys/ucontext.h>
+#include <unistd.h>
+
+#include "tombstone.h"
+
+extern "C" void __linker_use_fallback_allocator();
+
+extern "C" bool debuggerd_fallback(ucontext_t* ucontext, siginfo_t* siginfo, void* abort_message) {
+  // This is incredibly sketchy to do inside of a signal handler, especially when libbacktrace
+  // uses the C++ standard library throughout, but this code runs in the linker, so we'll be using
+  // the linker's malloc instead of the libc one. Switch it out for a replacement, just in case.
+  //
+  // This isn't the default method of dumping because it can fail in cases such as memory space
+  // exhaustion.
+  __linker_use_fallback_allocator();
+  engrave_tombstone_ucontext(-1, getpid(), gettid(), reinterpret_cast<uintptr_t>(abort_message),
+                             siginfo, ucontext);
+  return true;
+}
diff --git a/debuggerd/handler/debuggerd_fallback_nop.cpp b/debuggerd/handler/debuggerd_fallback_nop.cpp
new file mode 100644
index 0000000..9b3053f
--- /dev/null
+++ b/debuggerd/handler/debuggerd_fallback_nop.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stddef.h>
+#include <sys/ucontext.h>
+#include <unistd.h>
+
+extern "C" bool debuggerd_fallback(ucontext_t*, siginfo_t*, void*) {
+  return false;
+}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 353f642..67c26e2 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -62,6 +62,8 @@
 
 #define CRASH_DUMP_PATH "/system/bin/" CRASH_DUMP_NAME
 
+extern "C" bool debuggerd_fallback(ucontext_t*, siginfo_t*, void*);
+
 static debuggerd_callbacks_t g_callbacks;
 
 // Mutex to ensure only one crashing thread dumps itself.
@@ -81,7 +83,7 @@
   va_start(args, fmt);
 
   char buf[4096];
-  vsnprintf(buf, sizeof(buf), fmt, args);
+  __libc_format_buffer_va_list(buf, sizeof(buf), fmt, args);
   fatal("%s: %s", buf, strerror(err));
 }
 
@@ -174,6 +176,41 @@
   return (old_action.sa_flags & SA_SIGINFO) != 0;
 }
 
+static void raise_caps() {
+  // Raise CapInh to match CapPrm, so that we can set the ambient bits.
+  __user_cap_header_struct capheader;
+  memset(&capheader, 0, sizeof(capheader));
+  capheader.version = _LINUX_CAPABILITY_VERSION_3;
+  capheader.pid = 0;
+
+  __user_cap_data_struct capdata[2];
+  if (capget(&capheader, &capdata[0]) == -1) {
+    fatal_errno("capget failed");
+  }
+
+  if (capdata[0].permitted != capdata[0].inheritable ||
+      capdata[1].permitted != capdata[1].inheritable) {
+    capdata[0].inheritable = capdata[0].permitted;
+    capdata[1].inheritable = capdata[1].permitted;
+
+    if (capset(&capheader, &capdata[0]) == -1) {
+      __libc_format_log(ANDROID_LOG_ERROR, "libc", "capset failed: %s", strerror(errno));
+    }
+  }
+
+  // Set the ambient capability bits so that crash_dump gets all of our caps and can ptrace us.
+  uint64_t capmask = capdata[0].inheritable;
+  capmask |= static_cast<uint64_t>(capdata[1].inheritable) << 32;
+  for (unsigned long i = 0; i < 64; ++i) {
+    if (capmask & (1ULL << i)) {
+      if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) != 0) {
+        __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to raise ambient capability %lu: %s",
+                          i, strerror(errno));
+      }
+    }
+  }
+}
+
 struct debugger_thread_info {
   bool crash_dump_started;
   pid_t crashing_tid;
@@ -217,14 +254,14 @@
     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);
-    }
+    raise_caps();
 
-    char buf[10];
-    snprintf(buf, sizeof(buf), "%d", thread_info->crashing_tid);
-    execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, buf, nullptr);
+    char main_tid[10];
+    char pseudothread_tid[10];
+    __libc_format_buffer(main_tid, sizeof(main_tid), "%d", thread_info->crashing_tid);
+    __libc_format_buffer(pseudothread_tid, sizeof(pseudothread_tid), "%d", thread_info->pseudothread_tid);
+
+    execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, nullptr);
 
     fatal_errno("exec failed");
   } else {
@@ -294,7 +331,7 @@
 
 // Handler that does crash dumping by forking and doing the processing in the child.
 // Do this by ptracing the relevant thread, and then execing debuggerd to do the actual dump.
-static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) {
+static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* context) {
   int ret = pthread_mutex_lock(&crash_mutex);
   if (ret != 0) {
     __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
@@ -324,18 +361,22 @@
 
   log_signal_summary(signal_number, info);
 
-  if (prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
-    // The process has NO_NEW_PRIVS enabled, so we can't transition to the crash_dump context.
-    __libc_format_log(ANDROID_LOG_INFO, "libc",
-                      "Suppressing debuggerd output because prctl(PR_GET_NO_NEW_PRIVS)==1");
-    resend_signal(info, false);
-    return;
-  }
-
   void* abort_message = nullptr;
   if (g_callbacks.get_abort_message) {
     abort_message = g_callbacks.get_abort_message();
   }
+
+  if (prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
+    ucontext_t* ucontext = static_cast<ucontext_t*>(context);
+    if (signal_number == DEBUGGER_SIGNAL || !debuggerd_fallback(ucontext, info, abort_message)) {
+      // The process has NO_NEW_PRIVS enabled, so we can't transition to the crash_dump context.
+      __libc_format_log(ANDROID_LOG_INFO, "libc",
+                        "Suppressing debuggerd output because prctl(PR_GET_NO_NEW_PRIVS)==1");
+    }
+    resend_signal(info, false);
+    return;
+  }
+
   // Populate si_value with the abort message address, if found.
   if (abort_message) {
     info->si_value.sival_ptr = abort_message;
@@ -349,6 +390,12 @@
     .info = info
   };
 
+  // Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us.
+  int orig_dumpable = prctl(PR_GET_DUMPABLE);
+  if (prctl(PR_SET_DUMPABLE, 1) != 0) {
+    fatal_errno("failed to set dumpable");
+  }
+
   // Essentially pthread_create without CLONE_FILES (see debuggerd_dispatch_pseudothread).
   pid_t child_pid =
     clone(debuggerd_dispatch_pseudothread, pseudothread_stack,
@@ -364,6 +411,11 @@
   // and then wait for it to finish.
   __futex_wait(&thread_info.pseudothread_tid, child_pid, nullptr);
 
+  // Restore PR_SET_DUMPABLE to its original value.
+  if (prctl(PR_SET_DUMPABLE, orig_dumpable) != 0) {
+    fatal_errno("failed to restore dumpable");
+  }
+
   // Signals can either be fatal or nonfatal.
   // For fatal signals, crash_dump will PTRACE_CONT us with the signal we
   // crashed with, so that processes using waitpid on us will see that we
diff --git a/debuggerd/libdebuggerd/include/tombstone.h b/debuggerd/libdebuggerd/include/tombstone.h
index 4ff24af..aed71de 100644
--- a/debuggerd/libdebuggerd/include/tombstone.h
+++ b/debuggerd/libdebuggerd/include/tombstone.h
@@ -35,8 +35,11 @@
 
 /* Creates a tombstone file and writes the crash dump to it. */
 void engrave_tombstone(int tombstone_fd, BacktraceMap* map,
-                       const OpenFilesList& open_files, pid_t pid, pid_t tid,
-                       const std::set<pid_t>& siblings, uintptr_t abort_msg_address,
+                       const OpenFilesList* open_files, pid_t pid, pid_t tid,
+                       const std::set<pid_t>* siblings, uintptr_t abort_msg_address,
                        std::string* amfd_data);
 
+void engrave_tombstone_ucontext(int tombstone_fd, pid_t pid, pid_t tid, uintptr_t abort_msg_address,
+                                siginfo_t* siginfo, ucontext_t* ucontext);
+
 #endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index ac2c0b6..3166bfc 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -220,14 +220,8 @@
   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));
-  if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) {
-    ALOGE("cannot get siginfo: %s\n", strerror(errno));
-    return;
-  }
-
+static void dump_signal_info(log_t* log, const siginfo_t* siginfo) {
+  const siginfo_t& si = *siginfo;
   char addr_desc[32]; // ", fault addr 0x1234"
   if (signal_has_si_addr(si.si_signo, si.si_code)) {
     snprintf(addr_desc, sizeof(addr_desc), "%p", si.si_addr);
@@ -241,6 +235,17 @@
   dump_probable_cause(log, si);
 }
 
+static void dump_signal_info(log_t* log, pid_t tid) {
+  siginfo_t si;
+  memset(&si, 0, sizeof(si));
+  if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) {
+    ALOGE("cannot get siginfo: %s\n", strerror(errno));
+    return;
+  }
+
+  dump_signal_info(log, &si);
+}
+
 static void dump_thread_info(log_t* log, pid_t pid, pid_t tid) {
   char path[64];
   char threadnamebuf[1024];
@@ -649,8 +654,8 @@
 
 // Dumps all information about the specified pid to the tombstone.
 static void dump_crash(log_t* log, BacktraceMap* map,
-                       const OpenFilesList& open_files, pid_t pid, pid_t tid,
-                       const std::set<pid_t>& siblings, uintptr_t abort_msg_address) {
+                       const OpenFilesList* open_files, pid_t pid, pid_t tid,
+                       const std::set<pid_t>* siblings, uintptr_t abort_msg_address) {
   // don't copy log messages to tombstone unless this is a dev device
   char value[PROPERTY_VALUE_MAX];
   property_get("ro.debuggable", value, "0");
@@ -664,14 +669,16 @@
     dump_logs(log, pid, 5);
   }
 
-  if (!siblings.empty()) {
-    for (pid_t sibling : siblings) {
+  if (siblings && !siblings->empty()) {
+    for (pid_t sibling : *siblings) {
       dump_thread(log, pid, sibling, map, 0, false);
     }
   }
 
-  _LOG(log, logtype::OPEN_FILES, "\nopen files:\n");
-  dump_open_files_list_to_log(open_files, log, "    ");
+  if (open_files) {
+    _LOG(log, logtype::OPEN_FILES, "\nopen files:\n");
+    dump_open_files_list_to_log(*open_files, log, "    ");
+  }
 
   if (want_logs) {
     dump_logs(log, pid, 0);
@@ -732,19 +739,34 @@
 }
 
 void engrave_tombstone(int tombstone_fd, BacktraceMap* map,
-                       const OpenFilesList& open_files, pid_t pid, pid_t tid,
-                       const std::set<pid_t>& siblings, uintptr_t abort_msg_address,
+                       const OpenFilesList* open_files, pid_t pid, pid_t tid,
+                       const std::set<pid_t>* siblings, uintptr_t abort_msg_address,
                        std::string* amfd_data) {
   log_t log;
   log.current_tid = tid;
   log.crashed_tid = tid;
-
-  if (tombstone_fd < 0) {
-    ALOGE("debuggerd: skipping tombstone write, nothing to do.\n");
-    return;
-  }
-
   log.tfd = tombstone_fd;
   log.amfd_data = amfd_data;
   dump_crash(&log, map, open_files, pid, tid, siblings, abort_msg_address);
 }
+
+void engrave_tombstone_ucontext(int tombstone_fd, pid_t pid, pid_t tid, uintptr_t abort_msg_address,
+                                siginfo_t* siginfo, ucontext_t* ucontext) {
+  log_t log;
+  log.current_tid = tid;
+  log.crashed_tid = tid;
+  log.tfd = tombstone_fd;
+  log.amfd_data = nullptr;
+
+  dump_thread_info(&log, pid, tid);
+  dump_signal_info(&log, siginfo);
+
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid));
+  dump_abort_message(backtrace.get(), &log, abort_msg_address);
+  // TODO: Dump registers from the ucontext.
+  if (backtrace->Unwind(0, ucontext)) {
+    dump_backtrace_and_stack(backtrace.get(), &log);
+  } else {
+    ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
+  }
+}
diff --git a/fs_mgr/.clang-format b/fs_mgr/.clang-format
new file mode 120000
index 0000000..4e6d9dd
--- /dev/null
+++ b/fs_mgr/.clang-format
@@ -0,0 +1 @@
+../init/.clang-format
\ No newline at end of file
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index 956c702..6939428 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -29,17 +29,11 @@
 LOCAL_C_INCLUDES := \
     $(LOCAL_PATH)/include \
     system/vold \
-    system/extras/ext4_utils \
-    bootable/recovery
+    system/extras/ext4_utils
 LOCAL_MODULE:= libfs_mgr
 LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_CFLAGS := -Werror
-ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
-  ifeq ($(TARGET_USES_MKE2FS), true)
-    LOCAL_CFLAGS += -DTARGET_USES_MKE2FS
-  endif
-endif
 ifneq (,$(filter userdebug,$(TARGET_BUILD_VARIANT)))
 LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
 endif
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index be84e8a..1768078 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -103,9 +103,7 @@
     char tmpmnt_opts[64] = "errors=remount-ro";
     const char *e2fsck_argv[] = {
         E2FSCK_BIN,
-#ifndef TARGET_USES_MKE2FS // "-f" only for old ext4 generation tool
         "-f",
-#endif
         "-y",
         blk_device
     };
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index dd08271..68efb00 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -51,14 +51,12 @@
     "%u %s %s %u %u "       \
     "%" PRIu64 " %" PRIu64 " %s %s %s "
 
-#define VERITY_TABLE_PARAMS(hashtree_desc, blk_device, digest, salt)  \
-    hashtree_desc.dm_verity_version, blk_device, blk_device,          \
-        hashtree_desc.data_block_size, hashtree_desc.hash_block_size, \
-        hashtree_desc.image_size /                                    \
-            hashtree_desc.data_block_size, /* num_data_blocks. */     \
-        hashtree_desc.tree_offset /                                   \
-            hashtree_desc.hash_block_size, /* hash_start_block. */    \
-        (char *)hashtree_desc.hash_algorithm, digest, salt
+#define VERITY_TABLE_PARAMS(hashtree_desc, blk_device, digest, salt)                        \
+    hashtree_desc.dm_verity_version, blk_device, blk_device, hashtree_desc.data_block_size, \
+        hashtree_desc.hash_block_size,                                                      \
+        hashtree_desc.image_size / hashtree_desc.data_block_size,  /* num_data_blocks. */   \
+        hashtree_desc.tree_offset / hashtree_desc.hash_block_size, /* hash_start_block. */  \
+        (char*)hashtree_desc.hash_algorithm, digest, salt
 
 #define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
 #define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
@@ -67,32 +65,28 @@
  *     <#opt_params> ignore_zero_blocks restart_on_corruption
  */
 #define VERITY_TABLE_OPT_DEFAULT_FORMAT "2 %s %s"
-#define VERITY_TABLE_OPT_DEFAULT_PARAMS \
-    VERITY_TABLE_OPT_IGNZERO, VERITY_TABLE_OPT_RESTART
+#define VERITY_TABLE_OPT_DEFAULT_PARAMS VERITY_TABLE_OPT_IGNZERO, VERITY_TABLE_OPT_RESTART
 
 /* The FEC (forward error correction) format of dm-verity optional parameters:
  *     <#opt_params> use_fec_from_device <fec_dev>
  *     fec_roots <num> fec_blocks <num> fec_start <offset>
  *     ignore_zero_blocks restart_on_corruption
  */
-#define VERITY_TABLE_OPT_FEC_FORMAT                              \
-    "10 use_fec_from_device %s fec_roots %u fec_blocks %" PRIu64 \
-    " fec_start %" PRIu64 " %s %s"
+#define VERITY_TABLE_OPT_FEC_FORMAT \
+    "10 use_fec_from_device %s fec_roots %u fec_blocks %" PRIu64 " fec_start %" PRIu64 " %s %s"
 
 /* Note that fec_blocks is the size that FEC covers, *not* the
  * size of the FEC data. Since we use FEC for everything up until
  * the FEC data, it's the same as the offset (fec_start).
  */
-#define VERITY_TABLE_OPT_FEC_PARAMS(hashtree_desc, blk_device) \
-    blk_device, hashtree_desc.fec_num_roots,                   \
-        hashtree_desc.fec_offset /                             \
-            hashtree_desc.data_block_size, /* fec_blocks */    \
-        hashtree_desc.fec_offset /                             \
-            hashtree_desc.data_block_size, /* fec_start */     \
+#define VERITY_TABLE_OPT_FEC_PARAMS(hashtree_desc, blk_device)                     \
+    blk_device, hashtree_desc.fec_num_roots,                                       \
+        hashtree_desc.fec_offset / hashtree_desc.data_block_size, /* fec_blocks */ \
+        hashtree_desc.fec_offset / hashtree_desc.data_block_size, /* fec_start */  \
         VERITY_TABLE_OPT_IGNZERO, VERITY_TABLE_OPT_RESTART
 
-AvbSlotVerifyData *fs_mgr_avb_verify_data = nullptr;
-AvbOps *fs_mgr_avb_ops = nullptr;
+AvbSlotVerifyData* fs_mgr_avb_verify_data = nullptr;
+AvbOps* fs_mgr_avb_ops = nullptr;
 
 enum HashAlgorithm {
     kInvalid = 0,
@@ -109,8 +103,7 @@
 
 androidboot_vbmeta fs_mgr_vbmeta_prop;
 
-static inline bool nibble_value(const char &c, uint8_t *value)
-{
+static inline bool nibble_value(const char& c, uint8_t* value) {
     FS_MGR_CHECK(value != nullptr);
 
     switch (c) {
@@ -130,10 +123,7 @@
     return true;
 }
 
-static bool hex_to_bytes(uint8_t *bytes,
-                         size_t bytes_len,
-                         const std::string &hex)
-{
+static bool hex_to_bytes(uint8_t* bytes, size_t bytes_len, const std::string& hex) {
     FS_MGR_CHECK(bytes != nullptr);
 
     if (hex.size() % 2 != 0) {
@@ -156,11 +146,10 @@
     return true;
 }
 
-static std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len)
-{
+static std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
     FS_MGR_CHECK(bytes != nullptr);
 
-    static const char *hex_digits = "0123456789abcdef";
+    static const char* hex_digits = "0123456789abcdef";
     std::string hex;
 
     for (size_t i = 0; i < bytes_len; i++) {
@@ -170,8 +159,7 @@
     return hex;
 }
 
-static bool load_vbmeta_prop(androidboot_vbmeta *vbmeta_prop)
-{
+static bool load_vbmeta_prop(androidboot_vbmeta* vbmeta_prop) {
     FS_MGR_CHECK(vbmeta_prop != nullptr);
 
     std::string cmdline;
@@ -180,19 +168,17 @@
     std::string hash_alg;
     std::string digest;
 
-    for (const auto &entry :
-         android::base::Split(android::base::Trim(cmdline), " ")) {
+    for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
         std::vector<std::string> pieces = android::base::Split(entry, "=");
-        const std::string &key = pieces[0];
-        const std::string &value = pieces[1];
+        const std::string& key = pieces[0];
+        const std::string& value = pieces[1];
 
         if (key == "androidboot.vbmeta.device_state") {
             vbmeta_prop->allow_verification_error = (value == "unlocked");
         } else if (key == "androidboot.vbmeta.hash_alg") {
             hash_alg = value;
         } else if (key == "androidboot.vbmeta.size") {
-            if (!android::base::ParseUint(value.c_str(),
-                                          &vbmeta_prop->vbmeta_size)) {
+            if (!android::base::ParseUint(value.c_str(), &vbmeta_prop->vbmeta_size)) {
                 return false;
             }
         } else if (key == "androidboot.vbmeta.digest") {
@@ -220,10 +206,8 @@
         return false;
     }
 
-    if (!hex_to_bytes(vbmeta_prop->digest, sizeof(vbmeta_prop->digest),
-                      digest)) {
-        LERROR << "Hash digest contains non-hexidecimal character: "
-               << digest.c_str();
+    if (!hex_to_bytes(vbmeta_prop->digest, sizeof(vbmeta_prop->digest), digest)) {
+        LERROR << "Hash digest contains non-hexidecimal character: " << digest.c_str();
         return false;
     }
 
@@ -231,9 +215,8 @@
 }
 
 template <typename Hasher>
-static std::pair<size_t, bool> verify_vbmeta_digest(
-    const AvbSlotVerifyData &verify_data, const androidboot_vbmeta &vbmeta_prop)
-{
+static std::pair<size_t, bool> verify_vbmeta_digest(const AvbSlotVerifyData& verify_data,
+                                                    const androidboot_vbmeta& vbmeta_prop) {
     size_t total_size = 0;
     Hasher hasher;
     for (size_t n = 0; n < verify_data.num_vbmeta_images; n++) {
@@ -242,15 +225,13 @@
         total_size += verify_data.vbmeta_images[n].vbmeta_size;
     }
 
-    bool matched = (memcmp(hasher.finalize(), vbmeta_prop.digest,
-                           Hasher::DIGEST_SIZE) == 0);
+    bool matched = (memcmp(hasher.finalize(), vbmeta_prop.digest, Hasher::DIGEST_SIZE) == 0);
 
     return std::make_pair(total_size, matched);
 }
 
-static bool verify_vbmeta_images(const AvbSlotVerifyData &verify_data,
-                                 const androidboot_vbmeta &vbmeta_prop)
-{
+static bool verify_vbmeta_images(const AvbSlotVerifyData& verify_data,
+                                 const androidboot_vbmeta& vbmeta_prop) {
     if (verify_data.num_vbmeta_images == 0) {
         LERROR << "No vbmeta images";
         return false;
@@ -281,23 +262,17 @@
     return true;
 }
 
-static bool hashtree_load_verity_table(
-    struct dm_ioctl *io,
-    const std::string &dm_device_name,
-    int fd,
-    const std::string &blk_device,
-    const AvbHashtreeDescriptor &hashtree_desc,
-    const std::string &salt,
-    const std::string &root_digest)
-{
+static bool hashtree_load_verity_table(struct dm_ioctl* io, const std::string& dm_device_name,
+                                       int fd, const std::string& blk_device,
+                                       const AvbHashtreeDescriptor& hashtree_desc,
+                                       const std::string& salt, const std::string& root_digest) {
     fs_mgr_verity_ioctl_init(io, dm_device_name, DM_STATUS_TABLE_FLAG);
 
     // The buffer consists of [dm_ioctl][dm_target_spec][verity_params].
-    char *buffer = (char *)io;
+    char* buffer = (char*)io;
 
     // Builds the dm_target_spec arguments.
-    struct dm_target_spec *dm_target =
-        (struct dm_target_spec *)&buffer[sizeof(struct dm_ioctl)];
+    struct dm_target_spec* dm_target = (struct dm_target_spec*)&buffer[sizeof(struct dm_ioctl)];
     io->target_count = 1;
     dm_target->status = 0;
     dm_target->sector_start = 0;
@@ -305,23 +280,19 @@
     strcpy(dm_target->target_type, "verity");
 
     // Builds the verity params.
-    char *verity_params =
-        buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+    char* verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
     size_t bufsize = DM_BUF_SIZE - (verity_params - buffer);
 
     int res = 0;
     if (hashtree_desc.fec_size > 0) {
-        res = snprintf(
-            verity_params, bufsize,
-            VERITY_TABLE_FORMAT VERITY_TABLE_OPT_FEC_FORMAT,
-            VERITY_TABLE_PARAMS(hashtree_desc, blk_device.c_str(),
-                                root_digest.c_str(), salt.c_str()),
-            VERITY_TABLE_OPT_FEC_PARAMS(hashtree_desc, blk_device.c_str()));
+        res = snprintf(verity_params, bufsize, VERITY_TABLE_FORMAT VERITY_TABLE_OPT_FEC_FORMAT,
+                       VERITY_TABLE_PARAMS(hashtree_desc, blk_device.c_str(), root_digest.c_str(),
+                                           salt.c_str()),
+                       VERITY_TABLE_OPT_FEC_PARAMS(hashtree_desc, blk_device.c_str()));
     } else {
-        res = snprintf(verity_params, bufsize,
-                       VERITY_TABLE_FORMAT VERITY_TABLE_OPT_DEFAULT_FORMAT,
-                       VERITY_TABLE_PARAMS(hashtree_desc, blk_device.c_str(),
-                                           root_digest.c_str(), salt.c_str()),
+        res = snprintf(verity_params, bufsize, VERITY_TABLE_FORMAT VERITY_TABLE_OPT_DEFAULT_FORMAT,
+                       VERITY_TABLE_PARAMS(hashtree_desc, blk_device.c_str(), root_digest.c_str(),
+                                           salt.c_str()),
                        VERITY_TABLE_OPT_DEFAULT_PARAMS);
     }
 
@@ -334,7 +305,7 @@
 
     // Sets ext target boundary.
     verity_params += strlen(verity_params) + 1;
-    verity_params = (char *)(((unsigned long)verity_params + 7) & ~7);
+    verity_params = (char*)(((unsigned long)verity_params + 7) & ~7);
     dm_target->next = verity_params - buffer;
 
     // Sends the ioctl to load the verity table.
@@ -346,11 +317,9 @@
     return true;
 }
 
-static bool hashtree_dm_verity_setup(struct fstab_rec *fstab_entry,
-                                     const AvbHashtreeDescriptor &hashtree_desc,
-                                     const std::string &salt,
-                                     const std::string &root_digest)
-{
+static bool hashtree_dm_verity_setup(struct fstab_rec* fstab_entry,
+                                     const AvbHashtreeDescriptor& hashtree_desc,
+                                     const std::string& salt, const std::string& root_digest) {
     // Gets the device mapper fd.
     android::base::unique_fd fd(open("/dev/device-mapper", O_RDWR));
     if (fd < 0) {
@@ -360,7 +329,7 @@
 
     // Creates the device.
     alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
-    struct dm_ioctl *io = (struct dm_ioctl *)buffer;
+    struct dm_ioctl* io = (struct dm_ioctl*)buffer;
     const std::string mount_point(basename(fstab_entry->mount_point));
     if (!fs_mgr_create_verity_device(io, mount_point, fd)) {
         LERROR << "Couldn't create verity device!";
@@ -375,8 +344,7 @@
     }
 
     // Loads the verity mapping table.
-    if (!hashtree_load_verity_table(io, mount_point, fd,
-                                    std::string(fstab_entry->blk_device),
+    if (!hashtree_load_verity_table(io, mount_point, fd, std::string(fstab_entry->blk_device),
                                     hashtree_desc, salt, root_digest)) {
         LERROR << "Couldn't load verity table!";
         return false;
@@ -403,24 +371,20 @@
     return true;
 }
 
-static bool get_hashtree_descriptor(const std::string &partition_name,
-                                    const AvbSlotVerifyData &verify_data,
-                                    AvbHashtreeDescriptor *out_hashtree_desc,
-                                    std::string *out_salt,
-                                    std::string *out_digest)
-{
+static bool get_hashtree_descriptor(const std::string& partition_name,
+                                    const AvbSlotVerifyData& verify_data,
+                                    AvbHashtreeDescriptor* out_hashtree_desc, std::string* out_salt,
+                                    std::string* out_digest) {
     bool found = false;
-    const uint8_t *desc_partition_name;
+    const uint8_t* desc_partition_name;
 
     for (size_t i = 0; i < verify_data.num_vbmeta_images && !found; i++) {
         // Get descriptors from vbmeta_images[i].
         size_t num_descriptors;
-        std::unique_ptr<const AvbDescriptor *[], decltype(&avb_free)>
-            descriptors(
-                avb_descriptor_get_all(verify_data.vbmeta_images[i].vbmeta_data,
-                                       verify_data.vbmeta_images[i].vbmeta_size,
-                                       &num_descriptors),
-                avb_free);
+        std::unique_ptr<const AvbDescriptor* [], decltype(&avb_free)> descriptors(
+            avb_descriptor_get_all(verify_data.vbmeta_images[i].vbmeta_data,
+                                   verify_data.vbmeta_images[i].vbmeta_size, &num_descriptors),
+            avb_free);
 
         if (!descriptors || num_descriptors < 1) {
             continue;
@@ -428,12 +392,9 @@
 
         // Ensures that hashtree descriptor is either in /vbmeta or in
         // the same partition for verity setup.
-        std::string vbmeta_partition_name(
-            verify_data.vbmeta_images[i].partition_name);
-        if (vbmeta_partition_name != "vbmeta" &&
-            vbmeta_partition_name != partition_name) {
-            LWARNING << "Skip vbmeta image at "
-                     << verify_data.vbmeta_images[i].partition_name
+        std::string vbmeta_partition_name(verify_data.vbmeta_images[i].partition_name);
+        if (vbmeta_partition_name != "vbmeta" && vbmeta_partition_name != partition_name) {
+            LWARNING << "Skip vbmeta image at " << verify_data.vbmeta_images[i].partition_name
                      << " for partition: " << partition_name.c_str();
             continue;
         }
@@ -445,21 +406,18 @@
                 continue;
             }
             if (desc.tag == AVB_DESCRIPTOR_TAG_HASHTREE) {
-                desc_partition_name = (const uint8_t *)descriptors[j] +
-                                      sizeof(AvbHashtreeDescriptor);
+                desc_partition_name =
+                    (const uint8_t*)descriptors[j] + sizeof(AvbHashtreeDescriptor);
                 if (!avb_hashtree_descriptor_validate_and_byteswap(
-                        (AvbHashtreeDescriptor *)descriptors[j],
-                        out_hashtree_desc)) {
+                        (AvbHashtreeDescriptor*)descriptors[j], out_hashtree_desc)) {
                     continue;
                 }
-                if (out_hashtree_desc->partition_name_len !=
-                    partition_name.length()) {
+                if (out_hashtree_desc->partition_name_len != partition_name.length()) {
                     continue;
                 }
                 // Notes that desc_partition_name is not NUL-terminated.
-                std::string hashtree_partition_name(
-                    (const char *)desc_partition_name,
-                    out_hashtree_desc->partition_name_len);
+                std::string hashtree_partition_name((const char*)desc_partition_name,
+                                                    out_hashtree_desc->partition_name_len);
                 if (hashtree_partition_name == partition_name) {
                     found = true;
                 }
@@ -472,18 +430,16 @@
         return false;
     }
 
-    const uint8_t *desc_salt =
-        desc_partition_name + out_hashtree_desc->partition_name_len;
+    const uint8_t* desc_salt = desc_partition_name + out_hashtree_desc->partition_name_len;
     *out_salt = bytes_to_hex(desc_salt, out_hashtree_desc->salt_len);
 
-    const uint8_t *desc_digest = desc_salt + out_hashtree_desc->salt_len;
+    const uint8_t* desc_digest = desc_salt + out_hashtree_desc->salt_len;
     *out_digest = bytes_to_hex(desc_digest, out_hashtree_desc->root_digest_len);
 
     return true;
 }
 
-static bool init_is_avb_used()
-{
+static bool init_is_avb_used() {
     // When AVB is used, boot loader should set androidboot.vbmeta.{hash_alg,
     // size, digest} in kernel cmdline. They will then be imported by init
     // process to system properties: ro.boot.vbmeta.{hash_alg, size, digest}.
@@ -493,8 +449,7 @@
     // be done in fs_mgr_load_vbmeta_images() and FS_MGR_SETUP_AVB_FAIL will
     // be returned when there is an error.
 
-    std::string hash_alg =
-        android::base::GetProperty("ro.boot.vbmeta.hash_alg", "");
+    std::string hash_alg = android::base::GetProperty("ro.boot.vbmeta.hash_alg", "");
 
     if (hash_alg == "sha256" || hash_alg == "sha512") {
         return true;
@@ -503,14 +458,12 @@
     return false;
 }
 
-bool fs_mgr_is_avb_used()
-{
+bool fs_mgr_is_avb_used() {
     static bool result = init_is_avb_used();
     return result;
 }
 
-int fs_mgr_load_vbmeta_images(struct fstab *fstab)
-{
+int fs_mgr_load_vbmeta_images(struct fstab* fstab) {
     FS_MGR_CHECK(fstab != nullptr);
 
     // Gets the expected hash value of vbmeta images from
@@ -529,12 +482,11 @@
     // Sets requested_partitions to nullptr as it's to copy the contents
     // of HASH partitions into fs_mgr_avb_verify_data, which is not required as
     // fs_mgr only deals with HASHTREE partitions.
-    const char *requested_partitions[] = {nullptr};
-    const char *ab_suffix =
-        android::base::GetProperty("ro.boot.slot_suffix", "").c_str();
-    AvbSlotVerifyResult verify_result = avb_slot_verify(
-        fs_mgr_avb_ops, requested_partitions, ab_suffix,
-        fs_mgr_vbmeta_prop.allow_verification_error, &fs_mgr_avb_verify_data);
+    const char* requested_partitions[] = {nullptr};
+    const char* ab_suffix = android::base::GetProperty("ro.boot.slot_suffix", "").c_str();
+    AvbSlotVerifyResult verify_result =
+        avb_slot_verify(fs_mgr_avb_ops, requested_partitions, ab_suffix,
+                        fs_mgr_vbmeta_prop.allow_verification_error, &fs_mgr_avb_verify_data);
 
     // Only allow two verify results:
     //   - AVB_SLOT_VERIFY_RESULT_OK.
@@ -557,12 +509,11 @@
         // Checks whether FLAGS_HASHTREE_DISABLED is set.
         AvbVBMetaImageHeader vbmeta_header;
         avb_vbmeta_image_header_to_host_byte_order(
-            (AvbVBMetaImageHeader *)fs_mgr_avb_verify_data->vbmeta_images[0]
-                .vbmeta_data,
+            (AvbVBMetaImageHeader*)fs_mgr_avb_verify_data->vbmeta_images[0].vbmeta_data,
             &vbmeta_header);
 
-        bool hashtree_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags &
-                                  AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+        bool hashtree_disabled =
+            ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
         if (hashtree_disabled) {
             return FS_MGR_SETUP_AVB_HASHTREE_DISABLED;
         }
@@ -577,8 +528,7 @@
     return FS_MGR_SETUP_AVB_FAIL;
 }
 
-void fs_mgr_unload_vbmeta_images()
-{
+void fs_mgr_unload_vbmeta_images() {
     if (fs_mgr_avb_verify_data != nullptr) {
         avb_slot_verify_data_free(fs_mgr_avb_verify_data);
     }
@@ -588,32 +538,27 @@
     }
 }
 
-int fs_mgr_setup_avb(struct fstab_rec *fstab_entry)
-{
-    if (!fstab_entry || !fs_mgr_avb_verify_data ||
-        fs_mgr_avb_verify_data->num_vbmeta_images < 1) {
+int fs_mgr_setup_avb(struct fstab_rec* fstab_entry) {
+    if (!fstab_entry || !fs_mgr_avb_verify_data || fs_mgr_avb_verify_data->num_vbmeta_images < 1) {
         return FS_MGR_SETUP_AVB_FAIL;
     }
 
     std::string partition_name(basename(fstab_entry->mount_point));
-    if (!avb_validate_utf8((const uint8_t *)partition_name.c_str(),
-                           partition_name.length())) {
-        LERROR << "Partition name: " << partition_name.c_str()
-               << " is not valid UTF-8.";
+    if (!avb_validate_utf8((const uint8_t*)partition_name.c_str(), partition_name.length())) {
+        LERROR << "Partition name: " << partition_name.c_str() << " is not valid UTF-8.";
         return FS_MGR_SETUP_AVB_FAIL;
     }
 
     AvbHashtreeDescriptor hashtree_descriptor;
     std::string salt;
     std::string root_digest;
-    if (!get_hashtree_descriptor(partition_name, *fs_mgr_avb_verify_data,
-                                 &hashtree_descriptor, &salt, &root_digest)) {
+    if (!get_hashtree_descriptor(partition_name, *fs_mgr_avb_verify_data, &hashtree_descriptor,
+                                 &salt, &root_digest)) {
         return FS_MGR_SETUP_AVB_FAIL;
     }
 
     // Converts HASHTREE descriptor to verity_table_params.
-    if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt,
-                                  root_digest)) {
+    if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt, root_digest)) {
         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 f96f124..dd8c7e7 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -39,15 +39,11 @@
 #include "fs_mgr_avb_ops.h"
 #include "fs_mgr_priv.h"
 
-static struct fstab *fs_mgr_fstab = nullptr;
+static struct fstab* fs_mgr_fstab = nullptr;
 
-static AvbIOResult read_from_partition(AvbOps *ops ATTRIBUTE_UNUSED,
-                                       const char *partition,
-                                       int64_t offset,
-                                       size_t num_bytes,
-                                       void *buffer,
-                                       size_t *out_num_read)
-{
+static AvbIOResult read_from_partition(AvbOps* ops ATTRIBUTE_UNUSED, const char* partition,
+                                       int64_t offset, size_t num_bytes, void* buffer,
+                                       size_t* out_num_read) {
     // The input |partition| name is with ab_suffix, e.g. system_a.
     // Slot suffix (e.g. _a) will be appended to the device file path
     // for partitions having 'slotselect' optin in fstab file, but it
@@ -62,8 +58,7 @@
     //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
     //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a
 
-    struct fstab_rec *fstab_entry =
-        fs_mgr_get_entry_for_mount_point(fs_mgr_fstab, "/misc");
+    struct fstab_rec* fstab_entry = fs_mgr_get_entry_for_mount_point(fs_mgr_fstab, "/misc");
 
     if (fstab_entry == nullptr) {
         LERROR << "/misc mount point not found in fstab";
@@ -86,8 +81,7 @@
         return AVB_IO_RESULT_ERROR_IO;
     }
 
-    android::base::unique_fd fd(
-        TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
 
     if (fd < 0) {
         PERROR << "Failed to open " << path.c_str();
@@ -112,12 +106,11 @@
 
     // On Linux, we never get partial reads from block devices (except
     // for EOF).
-    ssize_t num_read =
-        TEMP_FAILURE_RETRY(pread64(fd, buffer, num_bytes, offset));
+    ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, buffer, num_bytes, offset));
 
     if (num_read < 0 || (size_t)num_read != num_bytes) {
-        PERROR << "Failed to read " << num_bytes << " bytes from "
-               << path.c_str() << " offset " << offset;
+        PERROR << "Failed to read " << num_bytes << " bytes from " << path.c_str() << " offset "
+               << offset;
         return AVB_IO_RESULT_ERROR_IO;
     }
 
@@ -128,11 +121,9 @@
     return AVB_IO_RESULT_OK;
 }
 
-static AvbIOResult dummy_read_rollback_index(AvbOps *ops ATTRIBUTE_UNUSED,
-                                             size_t rollback_index_location
-                                                 ATTRIBUTE_UNUSED,
-                                             uint64_t *out_rollback_index)
-{
+static AvbIOResult dummy_read_rollback_index(AvbOps* ops ATTRIBUTE_UNUSED,
+                                             size_t rollback_index_location ATTRIBUTE_UNUSED,
+                                             uint64_t* out_rollback_index) {
     // rollback_index has been checked in bootloader phase.
     // In user-space, returns the smallest value 0 to pass the check.
     *out_rollback_index = 0;
@@ -140,13 +131,9 @@
 }
 
 static AvbIOResult dummy_validate_vbmeta_public_key(
-    AvbOps *ops ATTRIBUTE_UNUSED,
-    const uint8_t *public_key_data ATTRIBUTE_UNUSED,
-    size_t public_key_length ATTRIBUTE_UNUSED,
-    const uint8_t *public_key_metadata ATTRIBUTE_UNUSED,
-    size_t public_key_metadata_length ATTRIBUTE_UNUSED,
-    bool *out_is_trusted)
-{
+    AvbOps* ops ATTRIBUTE_UNUSED, const uint8_t* public_key_data ATTRIBUTE_UNUSED,
+    size_t public_key_length ATTRIBUTE_UNUSED, const uint8_t* public_key_metadata ATTRIBUTE_UNUSED,
+    size_t public_key_metadata_length ATTRIBUTE_UNUSED, bool* out_is_trusted) {
     // vbmeta public key has been checked in bootloader phase.
     // In user-space, returns true to pass the check.
     //
@@ -158,9 +145,8 @@
     return AVB_IO_RESULT_OK;
 }
 
-static AvbIOResult dummy_read_is_device_unlocked(AvbOps *ops ATTRIBUTE_UNUSED,
-                                                 bool *out_is_unlocked)
-{
+static AvbIOResult dummy_read_is_device_unlocked(AvbOps* ops ATTRIBUTE_UNUSED,
+                                                 bool* out_is_unlocked) {
     // The function is for bootloader to update the value into
     // androidboot.vbmeta.device_state in kernel cmdline.
     // In user-space, returns true as we don't need to update it anymore.
@@ -168,12 +154,9 @@
     return AVB_IO_RESULT_OK;
 }
 
-static AvbIOResult dummy_get_unique_guid_for_partition(
-    AvbOps *ops ATTRIBUTE_UNUSED,
-    const char *partition ATTRIBUTE_UNUSED,
-    char *guid_buf,
-    size_t guid_buf_size)
-{
+static AvbIOResult dummy_get_unique_guid_for_partition(AvbOps* ops ATTRIBUTE_UNUSED,
+                                                       const char* partition ATTRIBUTE_UNUSED,
+                                                       char* guid_buf, size_t guid_buf_size) {
     // The function is for bootloader to set the correct UUID
     // for a given partition in kernel cmdline.
     // In user-space, returns a faking one as we don't need to update
@@ -182,14 +165,13 @@
     return AVB_IO_RESULT_OK;
 }
 
-AvbOps *fs_mgr_dummy_avb_ops_new(struct fstab *fstab)
-{
-    AvbOps *ops;
+AvbOps* fs_mgr_dummy_avb_ops_new(struct fstab* fstab) {
+    AvbOps* ops;
 
     // Assigns the fstab to the static variable for later use.
     fs_mgr_fstab = fstab;
 
-    ops = (AvbOps *)calloc(1, sizeof(AvbOps));
+    ops = (AvbOps*)calloc(1, sizeof(AvbOps));
     if (ops == nullptr) {
         LERROR << "Error allocating memory for AvbOps";
         return nullptr;
@@ -207,7 +189,4 @@
     return ops;
 }
 
-void fs_mgr_dummy_avb_ops_free(AvbOps *ops)
-{
-    free(ops);
-}
+void fs_mgr_dummy_avb_ops_free(AvbOps* ops) { free(ops); }
diff --git a/fs_mgr/fs_mgr_avb_ops.h b/fs_mgr/fs_mgr_avb_ops.h
index 9f99be8..bfdec9a 100644
--- a/fs_mgr/fs_mgr_avb_ops.h
+++ b/fs_mgr/fs_mgr_avb_ops.h
@@ -49,10 +49,10 @@
  *
  * Frees with fs_mgr_dummy_avb_ops_free().
  */
-AvbOps *fs_mgr_dummy_avb_ops_new(struct fstab *fstab);
+AvbOps* fs_mgr_dummy_avb_ops_new(struct fstab* fstab);
 
 /* Frees an AvbOps instance previously allocated with fs_mgr_avb_ops_new(). */
-void fs_mgr_dummy_avb_ops_free(AvbOps *ops);
+void fs_mgr_dummy_avb_ops_free(AvbOps* ops);
 
 __END_DECLS
 
diff --git a/fs_mgr/fs_mgr_dm_ioctl.cpp b/fs_mgr/fs_mgr_dm_ioctl.cpp
index 6012a39..4cbd5a8 100644
--- a/fs_mgr/fs_mgr_dm_ioctl.cpp
+++ b/fs_mgr/fs_mgr_dm_ioctl.cpp
@@ -23,10 +23,7 @@
 #include "fs_mgr_priv.h"
 #include "fs_mgr_priv_dm_ioctl.h"
 
-void fs_mgr_verity_ioctl_init(struct dm_ioctl *io,
-                              const std::string &name,
-                              unsigned flags)
-{
+void fs_mgr_verity_ioctl_init(struct dm_ioctl* io, const std::string& name, unsigned flags) {
     memset(io, 0, DM_BUF_SIZE);
     io->data_size = DM_BUF_SIZE;
     io->data_start = sizeof(struct dm_ioctl);
@@ -39,10 +36,7 @@
     }
 }
 
-bool fs_mgr_create_verity_device(struct dm_ioctl *io,
-                                 const std::string &name,
-                                 int fd)
-{
+bool fs_mgr_create_verity_device(struct dm_ioctl* io, const std::string& name, int fd) {
     fs_mgr_verity_ioctl_init(io, name, 1);
     if (ioctl(fd, DM_DEV_CREATE, io)) {
         PERROR << "Error creating device mapping";
@@ -51,10 +45,7 @@
     return true;
 }
 
-bool fs_mgr_destroy_verity_device(struct dm_ioctl *io,
-                                  const std::string &name,
-                                  int fd)
-{
+bool fs_mgr_destroy_verity_device(struct dm_ioctl* io, const std::string& name, int fd) {
     fs_mgr_verity_ioctl_init(io, name, 0);
     if (ioctl(fd, DM_DEV_REMOVE, io)) {
         PERROR << "Error removing device mapping";
@@ -63,11 +54,8 @@
     return true;
 }
 
-bool fs_mgr_get_verity_device_name(struct dm_ioctl *io,
-                                   const std::string &name,
-                                   int fd,
-                                   std::string *out_dev_name)
-{
+bool fs_mgr_get_verity_device_name(struct dm_ioctl* io, const std::string& name, int fd,
+                                   std::string* out_dev_name) {
     FS_MGR_CHECK(out_dev_name != nullptr);
 
     fs_mgr_verity_ioctl_init(io, name, 0);
@@ -82,10 +70,7 @@
     return true;
 }
 
-bool fs_mgr_resume_verity_table(struct dm_ioctl *io,
-                                const std::string &name,
-                                int fd)
-{
+bool fs_mgr_resume_verity_table(struct dm_ioctl* io, const std::string& name, int fd) {
     fs_mgr_verity_ioctl_init(io, name, 0);
     if (ioctl(fd, DM_DEV_SUSPEND, io)) {
         PERROR << "Error activating verity device";
diff --git a/fs_mgr/fs_mgr_priv_avb.h b/fs_mgr/fs_mgr_priv_avb.h
index 6d0171c..dce9f61 100644
--- a/fs_mgr/fs_mgr_priv_avb.h
+++ b/fs_mgr/fs_mgr_priv_avb.h
@@ -45,11 +45,11 @@
  *     developers to make the filesystem writable to allow replacing
  *     binaries on the device.
  */
-int fs_mgr_load_vbmeta_images(struct fstab *fstab);
+int fs_mgr_load_vbmeta_images(struct fstab* fstab);
 
 void fs_mgr_unload_vbmeta_images();
 
-int fs_mgr_setup_avb(struct fstab_rec *fstab_entry);
+int fs_mgr_setup_avb(struct fstab_rec* fstab_entry);
 
 __END_DECLS
 
diff --git a/fs_mgr/fs_mgr_priv_dm_ioctl.h b/fs_mgr/fs_mgr_priv_dm_ioctl.h
index eeae4dd..a00a9c1 100644
--- a/fs_mgr/fs_mgr_priv_dm_ioctl.h
+++ b/fs_mgr/fs_mgr_priv_dm_ioctl.h
@@ -17,28 +17,18 @@
 #ifndef __CORE_FS_MGR_PRIV_DM_IOCTL_H
 #define __CORE_FS_MGR_PRIV_DM_IOCTL_H
 
-#include <string>
 #include <linux/dm-ioctl.h>
+#include <string>
 
-void fs_mgr_verity_ioctl_init(struct dm_ioctl *io,
-                              const std::string &name,
-                              unsigned flags);
+void fs_mgr_verity_ioctl_init(struct dm_ioctl* io, const std::string& name, unsigned flags);
 
-bool fs_mgr_create_verity_device(struct dm_ioctl *io,
-                                 const std::string &name,
-                                 int fd);
+bool fs_mgr_create_verity_device(struct dm_ioctl* io, const std::string& name, int fd);
 
-bool fs_mgr_destroy_verity_device(struct dm_ioctl *io,
-                                  const std::string &name,
-                                  int fd);
+bool fs_mgr_destroy_verity_device(struct dm_ioctl* io, const std::string& name, int fd);
 
-bool fs_mgr_get_verity_device_name(struct dm_ioctl *io,
-                                   const std::string &name,
-                                   int fd,
-                                   std::string *out_dev_name);
+bool fs_mgr_get_verity_device_name(struct dm_ioctl* io, const std::string& name, int fd,
+                                   std::string* out_dev_name);
 
-bool fs_mgr_resume_verity_table(struct dm_ioctl *io,
-                                const std::string &name,
-                                int fd);
+bool fs_mgr_resume_verity_table(struct dm_ioctl* io, const std::string& name, int fd);
 
 #endif /* __CORE_FS_MGR_PRIV_DM_IOCTL_H */
diff --git a/fs_mgr/fs_mgr_priv_sha.h b/fs_mgr/fs_mgr_priv_sha.h
index 1abc273..882411b 100644
--- a/fs_mgr/fs_mgr_priv_sha.h
+++ b/fs_mgr/fs_mgr_priv_sha.h
@@ -19,8 +19,7 @@
 
 #include <openssl/sha.h>
 
-class SHA256Hasher
-{
+class SHA256Hasher {
    private:
     SHA256_CTX sha256_ctx;
     uint8_t hash[SHA256_DIGEST_LENGTH];
@@ -28,25 +27,17 @@
    public:
     enum { DIGEST_SIZE = SHA256_DIGEST_LENGTH };
 
-    SHA256Hasher()
-    {
-        SHA256_Init(&sha256_ctx);
-    }
+    SHA256Hasher() { SHA256_Init(&sha256_ctx); }
 
-    void update(const void *data, size_t data_size)
-    {
-        SHA256_Update(&sha256_ctx, data, data_size);
-    }
+    void update(const void* data, size_t data_size) { SHA256_Update(&sha256_ctx, data, data_size); }
 
-    const uint8_t *finalize()
-    {
+    const uint8_t* finalize() {
         SHA256_Final(hash, &sha256_ctx);
         return hash;
     }
 };
 
-class SHA512Hasher
-{
+class SHA512Hasher {
    private:
     SHA512_CTX sha512_ctx;
     uint8_t hash[SHA512_DIGEST_LENGTH];
@@ -54,18 +45,13 @@
    public:
     enum { DIGEST_SIZE = SHA512_DIGEST_LENGTH };
 
-    SHA512Hasher()
-    {
-        SHA512_Init(&sha512_ctx);
-    }
+    SHA512Hasher() { SHA512_Init(&sha512_ctx); }
 
-    void update(const uint8_t *data, size_t data_size)
-    {
+    void update(const uint8_t* data, size_t data_size) {
         SHA512_Update(&sha512_ctx, data, data_size);
     }
 
-    const uint8_t *finalize()
-    {
+    const uint8_t* finalize() {
         SHA512_Final(hash, &sha512_ctx);
         return hash;
     }
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index e957f6b..b30417f 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -33,53 +33,6 @@
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
 
-#include "bootloader.h"
-
-// Copies slot_suffix from misc into |out_suffix|. Returns 0 on
-// success, -1 on error or if there is no non-empty slot_suffix.
-static int get_active_slot_suffix_from_misc(struct fstab *fstab,
-                                            char *out_suffix,
-                                            size_t suffix_len)
-{
-    int n;
-    int misc_fd;
-    ssize_t num_read;
-    struct bootloader_message_ab msg;
-
-    misc_fd = -1;
-    for (n = 0; n < fstab->num_entries; n++) {
-        if (strcmp(fstab->recs[n].mount_point, "/misc") == 0) {
-            misc_fd = open(fstab->recs[n].blk_device, O_RDONLY);
-            if (misc_fd == -1) {
-                PERROR << "Error opening misc partition '"
-                       << fstab->recs[n].blk_device << "'";
-                return -1;
-            } else {
-                break;
-            }
-        }
-    }
-
-    if (misc_fd == -1) {
-        LERROR << "Error finding misc partition";
-        return -1;
-    }
-
-    num_read = TEMP_FAILURE_RETRY(read(misc_fd, &msg, sizeof(msg)));
-    // Linux will never return partial reads when reading from block
-    // devices so no need to worry about them.
-    if (num_read != sizeof(msg)) {
-        PERROR << "Error reading bootloader_message";
-        close(misc_fd);
-        return -1;
-    }
-    close(misc_fd);
-    if (msg.slot_suffix[0] == '\0')
-        return -1;
-    strncpy(out_suffix, msg.slot_suffix, suffix_len);
-    return 0;
-}
-
 // finds slot_suffix in androidboot.slot_suffix kernel command line argument
 // or in the device tree node at /firmware/android/slot_suffix property
 static int get_active_slot_suffix_from_kernel(char *out_suffix,
@@ -123,11 +76,10 @@
     return -1;
 }
 
-// Gets slot_suffix from either the kernel cmdline / device tree / firmware
-// or the misc partition. Sets |out_suffix| on success and returns 0. Returns
-// -1 if slot_suffix could not be determined.
-static int get_active_slot_suffix(struct fstab *fstab, char *out_suffix,
-                                  size_t suffix_len)
+// Gets slot_suffix from either the kernel cmdline / device tree.  Sets
+// |out_suffix| on success and returns 0. Returns -1 if slot_suffix could not
+// be determined.
+static int get_active_slot_suffix(char *out_suffix, size_t suffix_len)
 {
     char propbuf[PROPERTY_VALUE_MAX];
 
@@ -140,22 +92,14 @@
         return 0;
     }
 
-    // if the property is not set, we are either being invoked too early
-    // or the slot suffix in mentioned in the misc partition. If its
-    // "too early", try to find the slotsuffix ourselves in the kernel command
-    // line or the device tree
+    // if the property is not set, we are probably being invoked early during
+    // boot.  Try to find the slotsuffix ourselves in the kernel command line
+    // or the device tree
     if (get_active_slot_suffix_from_kernel(out_suffix, suffix_len) == 0) {
         LINFO << "Using slot suffix '" << out_suffix << "' from kernel";
         return 0;
     }
 
-    // If we couldn't get the suffix from the kernel cmdline, try the
-    // the misc partition.
-    if (get_active_slot_suffix_from_misc(fstab, out_suffix, suffix_len) == 0) {
-        LINFO << "Using slot suffix '" << out_suffix << "' from misc";
-        return 0;
-    }
-
     LERROR << "Error determining slot_suffix";
 
     return -1;
@@ -174,8 +118,7 @@
 
             if (!got_suffix) {
                 memset(suffix, '\0', sizeof(suffix));
-                if (get_active_slot_suffix(fstab, suffix,
-                                           sizeof(suffix) - 1) != 0) {
+                if (get_active_slot_suffix(suffix, sizeof(suffix) - 1) != 0) {
                   return -1;
                 }
                 got_suffix = 1;
diff --git a/init/init.cpp b/init/init.cpp
index 9c1e23b..7f7eb2f 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -47,6 +47,7 @@
 #include <cutils/iosched_policy.h>
 #include <cutils/list.h>
 #include <cutils/sockets.h>
+#include <libavb/libavb.h>
 #include <private/android_filesystem_config.h>
 
 #include <fstream>
@@ -959,6 +960,9 @@
         property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
         property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
 
+        // Set libavb version for Framework-only OTA match in Treble build.
+        property_set("ro.boot.init.avb_version", std::to_string(AVB_MAJOR_VERSION).c_str());
+
         // Clean up our environment.
         unsetenv("INIT_SECOND_STAGE");
         unsetenv("INIT_STARTED_AT");
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 8ba7452..cf31195 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -60,7 +60,7 @@
         "native_handle.c",
         "open_memstream.c",
         "record_stream.c",
-        "sched_policy.c",
+        "sched_policy.cpp",
         "sockets.cpp",
         "strdup16to8.c",
         "strdup8to16.c",
@@ -141,14 +141,6 @@
     header_libs: ["libcutils_headers"],
     export_header_lib_headers: ["libcutils_headers"],
 
-    product_variables: {
-        cpusets: {
-            cflags: ["-DUSE_CPUSETS"],
-        },
-        schedboost: {
-            cflags: ["-DUSE_SCHEDBOOST"],
-        },
-    },
     cflags: [
         "-Werror",
         "-Wall",
diff --git a/libcutils/include/cutils/sched_policy.h b/libcutils/include/cutils/sched_policy.h
index 591bd44..15391d9 100644
--- a/libcutils/include/cutils/sched_policy.h
+++ b/libcutils/include/cutils/sched_policy.h
@@ -17,10 +17,27 @@
 #ifndef __CUTILS_SCHED_POLICY_H
 #define __CUTILS_SCHED_POLICY_H
 
+#include <stdbool.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/*
+ * Check if Linux kernel enables CPUSETS feature.
+ *
+ * Return value: 1 if Linux kernel CONFIG_CPUSETS=y; 0 otherwise.
+ */
+extern bool cpusets_enabled();
+
+/*
+ * Check if Linux kernel enables SCHEDTUNE feature (only available in Android
+ * common kernel or Linaro LSK, not in mainline Linux as of v4.9)
+ *
+ * Return value: 1 if Linux kernel CONFIG_SCHEDTUNE=y; 0 otherwise.
+ */
+extern bool schedboost_enabled();
+
 /* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */
 typedef enum {
     SP_DEFAULT    = -1,
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.cpp
similarity index 73%
rename from libcutils/sched_policy.c
rename to libcutils/sched_policy.cpp
index 5c5f3a5..7e3ad59 100644
--- a/libcutils/sched_policy.c
+++ b/libcutils/sched_policy.cpp
@@ -58,13 +58,11 @@
 static int bg_cgroup_fd = -1;
 static int fg_cgroup_fd = -1;
 
-#ifdef USE_CPUSETS
 // File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
 static int system_bg_cpuset_fd = -1;
 static int bg_cpuset_fd = -1;
 static int fg_cpuset_fd = -1;
 static int ta_cpuset_fd = -1; // special cpuset for top app
-#endif
 
 // File descriptors open to /dev/stune/../tasks, setup by initialize, or -1 on error
 static int bg_schedboost_fd = -1;
@@ -106,8 +104,53 @@
     return 0;
 }
 
-static void __initialize(void) {
-    char* filename;
+/*
+    If CONFIG_CPUSETS for Linux kernel is set, "tasks" can be found under
+    /dev/cpuset mounted in init.rc; otherwise, that file does not exist
+    even though the directory, /dev/cpuset, is still created (by init.rc).
+
+    A couple of other candidates (under cpuset mount directory):
+        notify_on_release
+        release_agent
+
+    Yet another way to decide if cpuset is enabled is to parse
+    /proc/self/status and search for lines begin with "Mems_allowed".
+
+    If CONFIG_PROC_PID_CPUSET is set, the existence "/proc/self/cpuset" can
+    be used to decide if CONFIG_CPUSETS is set, so we don't have a dependency
+    on where init.rc mounts cpuset. That's why we'd better require this
+    configuration be set if CONFIG_CPUSETS is set.
+
+    With runtime check using the following function, build time
+    variables like ENABLE_CPUSETS (used in Android.mk) or cpusets (used
+    in Android.bp) are not needed.
+ */
+
+bool cpusets_enabled() {
+    static bool enabled = (access("/dev/cpuset/tasks", F_OK) == 0);
+
+    return enabled;
+}
+
+/*
+    Similar to CONFIG_CPUSETS above, but with a different configuration
+    CONFIG_SCHEDTUNE that's in Android common Linux kernel and Linaro
+    Stable Kernel (LSK), but not in mainline Linux as of v4.9.
+
+    With runtime check using the following function, build time
+    variables like ENABLE_SCHEDBOOST (used in Android.mk) or schedboost
+    (used in Android.bp) are not needed.
+
+ */
+
+bool schedboost_enabled() {
+    static bool enabled = (access("/dev/stune/tasks", F_OK) == 0);
+
+    return enabled;
+}
+
+static void __initialize() {
+    const char* filename;
     if (!access("/dev/cpuctl/tasks", W_OK)) {
         __sys_supports_schedgroups = 1;
 
@@ -126,28 +169,28 @@
         __sys_supports_schedgroups = 0;
     }
 
-#ifdef USE_CPUSETS
-    if (!access("/dev/cpuset/tasks", W_OK)) {
+    if (cpusets_enabled()) {
+        if (!access("/dev/cpuset/tasks", W_OK)) {
 
-        filename = "/dev/cpuset/foreground/tasks";
-        fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
-        filename = "/dev/cpuset/background/tasks";
-        bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
-        filename = "/dev/cpuset/system-background/tasks";
-        system_bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
-        filename = "/dev/cpuset/top-app/tasks";
-        ta_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+            filename = "/dev/cpuset/foreground/tasks";
+            fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+            filename = "/dev/cpuset/background/tasks";
+            bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+            filename = "/dev/cpuset/system-background/tasks";
+            system_bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+            filename = "/dev/cpuset/top-app/tasks";
+            ta_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
 
-#ifdef USE_SCHEDBOOST
-        filename = "/dev/stune/top-app/tasks";
-        ta_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
-        filename = "/dev/stune/foreground/tasks";
-        fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
-        filename = "/dev/stune/background/tasks";
-        bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
-#endif
+            if (schedboost_enabled()) {
+                filename = "/dev/stune/top-app/tasks";
+                ta_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+                filename = "/dev/stune/foreground/tasks";
+                fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+                filename = "/dev/stune/background/tasks";
+                bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+            }
+        }
     }
-#endif
 
     char buf[64];
     snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", getpid());
@@ -235,33 +278,34 @@
 
     if (__sys_supports_schedgroups) {
         char grpBuf[32];
-#ifdef USE_CPUSETS
-        if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0)
-            return -1;
-        if (grpBuf[0] == '\0') {
-            *policy = SP_FOREGROUND;
-        } else if (!strcmp(grpBuf, "foreground")) {
-            *policy = SP_FOREGROUND;
-        } else if (!strcmp(grpBuf, "background")) {
-            *policy = SP_BACKGROUND;
-        } else if (!strcmp(grpBuf, "top-app")) {
-            *policy = SP_TOP_APP;
+
+        if (cpusets_enabled()) {
+            if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0)
+                return -1;
+            if (grpBuf[0] == '\0') {
+                *policy = SP_FOREGROUND;
+            } else if (!strcmp(grpBuf, "foreground")) {
+                *policy = SP_FOREGROUND;
+            } else if (!strcmp(grpBuf, "background")) {
+                *policy = SP_BACKGROUND;
+            } else if (!strcmp(grpBuf, "top-app")) {
+                *policy = SP_TOP_APP;
+            } else {
+                errno = ERANGE;
+                return -1;
+            }
         } else {
-            errno = ERANGE;
-            return -1;
+            if (getCGroupSubsys(tid, "cpu", grpBuf, sizeof(grpBuf)) < 0)
+                return -1;
+            if (grpBuf[0] == '\0') {
+                *policy = SP_FOREGROUND;
+            } else if (!strcmp(grpBuf, "bg_non_interactive")) {
+                *policy = SP_BACKGROUND;
+            } else {
+                errno = ERANGE;
+                return -1;
+            }
         }
-#else
-        if (getCGroupSubsys(tid, "cpu", grpBuf, sizeof(grpBuf)) < 0)
-            return -1;
-        if (grpBuf[0] == '\0') {
-            *policy = SP_FOREGROUND;
-        } else if (!strcmp(grpBuf, "bg_non_interactive")) {
-            *policy = SP_BACKGROUND;
-        } else {
-            errno = ERANGE;
-            return -1;
-        }
-#endif
     } else {
         int rc = sched_getscheduler(tid);
         if (rc < 0)
@@ -281,9 +325,10 @@
 int set_cpuset_policy(int tid, SchedPolicy policy)
 {
     // in the absence of cpusets, use the old sched policy
-#ifndef USE_CPUSETS
-    return set_sched_policy(tid, policy);
-#else
+    if (!cpusets_enabled()) {
+        return set_sched_policy(tid, policy);
+    }
+
     if (tid == 0) {
         tid = gettid();
     }
@@ -320,15 +365,14 @@
             return -errno;
     }
 
-#ifdef USE_SCHEDBOOST
-    if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
-        if (errno != ESRCH && errno != ENOENT)
-            return -errno;
+    if (schedboost_enabled()) {
+        if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
+            if (errno != ESRCH && errno != ENOENT)
+                return -errno;
+        }
     }
-#endif
 
     return 0;
-#endif
 }
 
 static void set_timerslack_ns(int tid, unsigned long long slack) {
@@ -420,18 +464,17 @@
             break;
         }
 
-
         if (add_tid_to_cgroup(tid, fd) != 0) {
             if (errno != ESRCH && errno != ENOENT)
                 return -errno;
         }
 
-#ifdef USE_SCHEDBOOST
-        if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
-            if (errno != ESRCH && errno != ENOENT)
-                return -errno;
+        if (schedboost_enabled()) {
+            if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
+                if (errno != ESRCH && errno != ENOENT)
+                    return -errno;
+            }
         }
-#endif
     } else {
         struct sched_param param;
 
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.c
index ccc7da8..a6c3f7a 100644
--- a/liblog/logd_reader.c
+++ b/liblog/logd_reader.c
@@ -91,7 +91,7 @@
 
 static int logdAvailable(log_id_t logId)
 {
-    if (logId > LOG_ID_KERNEL) {
+    if (logId >= LOG_ID_MAX) {
         return -EINVAL;
     }
     if (logId == LOG_ID_SECURITY) {
diff --git a/liblog/logd_writer.c b/liblog/logd_writer.c
index 2bab92e..12b797d 100644
--- a/liblog/logd_writer.c
+++ b/liblog/logd_writer.c
@@ -117,7 +117,7 @@
 
 static int logdAvailable(log_id_t logId)
 {
-    if (logId > LOG_ID_SECURITY) {
+    if (logId >= LOG_ID_MAX || logId == LOG_ID_KERNEL) {
         return -EINVAL;
     }
     if (atomic_load(&logdLoggerWrite.context.sock) < 0) {
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 1f8395b..4252ba6 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -22,7 +22,6 @@
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <typeinfo>
 #include <unistd.h>
 
 #include <utils/RefBase.h>
diff --git a/libutils/include/utils/Vector.h b/libutils/include/utils/Vector.h
index 28a77b8..9a643f9 100644
--- a/libutils/include/utils/Vector.h
+++ b/libutils/include/utils/Vector.h
@@ -20,8 +20,6 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <new>
-
 #include <log/log.h>
 #include <utils/TypeHelpers.h>
 #include <utils/VectorImpl.h>
diff --git a/libutils/primes.py b/libutils/primes.py
deleted file mode 100755
index e161dd8..0000000
--- a/libutils/primes.py
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python2.6
-#
-# Copyright (C) 2011 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-#
-# Generates a table of prime numbers for use in BasicHashtable.cpp.
-#
-# Each prime is chosen such that it is a little more than twice as large as
-# the previous prime in the table.  This makes it easier to choose a new
-# hashtable size when the underlying array is grown by as nominal factor
-# of two each time.
-#
-
-def is_odd_prime(n):
-  limit = (n - 1) / 2
-  d = 3
-  while d <= limit:
-    if n % d == 0:
-      return False
-    d += 2
-  return True
-
-print "static size_t PRIMES[] = {"
-
-n = 5
-max = 2**31 - 1
-while n < max:
-  print "    %d," % (n)
-  n = n * 2 + 1
-  while not is_odd_prime(n):
-    n += 2
-
-print "    0,"
-print "};"
diff --git a/logcat/.clang-format b/logcat/.clang-format
new file mode 100644
index 0000000..393c309
--- /dev/null
+++ b/logcat/.clang-format
@@ -0,0 +1,11 @@
+BasedOnStyle: Google
+AllowShortFunctionsOnASingleLine: false
+
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+PenaltyExcessCharacter: 32
+
+Cpp11BracedListStyle: false
diff --git a/logcat/Android.mk b/logcat/Android.mk
index 1dacbe1..723c35e 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -1,20 +1,31 @@
 # Copyright 2006-2014 The Android Open Source Project
 
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
+
+logcatLibs := liblog libbase libcutils libpcrecpp
+
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:= logcat.cpp event.logtags
-
-LOCAL_SHARED_LIBRARIES := liblog libbase libcutils libpcrecpp
-
 LOCAL_MODULE := logcat
-
+LOCAL_SRC_FILES := logcat_main.cpp event.logtags
+LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
 LOCAL_CFLAGS := -Werror
 
 include $(BUILD_EXECUTABLE)
 
 include $(CLEAR_VARS)
 
+LOCAL_MODULE := liblogcat
+LOCAL_SRC_FILES := logcat.cpp logcat_system.cpp
+LOCAL_SHARED_LIBRARIES := $(logcatLibs)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Werror
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
 LOCAL_MODULE := logpersist.start
 LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE_CLASS := EXECUTABLES
diff --git a/logcat/include/log/logcat.h b/logcat/include/log/logcat.h
new file mode 100644
index 0000000..009672c
--- /dev/null
+++ b/logcat/include/log/logcat.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_LOGCAT_H /* header boilerplate */
+#define _LIBS_LOGCAT_H
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
+#elif __ANDROID_API__ > 24 /* > Nougat */
+#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
+
+/* For managing an in-process logcat function, rather than forking/execing
+ *
+ * It also serves as the basis for the logcat command.
+ *
+ * The following C API allows a logcat instance to be created, run
+ * to completion, and then release all the associated resources.
+ */
+
+/*
+ * The opaque context
+ */
+#ifndef __android_logcat_context_defined /* typedef boilerplate */
+#define __android_logcat_context_defined
+typedef struct android_logcat_context_internal* android_logcat_context;
+#endif
+
+/* Creates a context associated with this logcat instance
+ *
+ * Returns a pointer to the context, or a NULL on error.
+ */
+android_logcat_context create_android_logcat();
+
+/* Collects and outputs the logcat data to output and error file descriptors
+ *
+ * Will block, performed in-thread and in-process
+ *
+ * The output file descriptor variable, if greater than or equal to 0, is
+ * where the output (ie: stdout) will be sent. The file descriptor is closed
+ * on android_logcat_destroy which terminates the instance, or when an -f flag
+ * (output redirect to a file) is present in the command.  The error file
+ * descriptor variable, if greater than or equal to 0, is where the error
+ * stream (ie: stderr) will be sent, also closed on android_logcat_destroy.
+ * The error file descriptor can be set to equal to the output file descriptor,
+ * which will mix output and error stream content, and will defer closure of
+ * the file descriptor on -f flag redirection.  Negative values for the file
+ * descriptors will use stdout and stderr FILE references respectively
+ * internally, and will not close the references as noted above.
+ *
+ * Return value is 0 for success, non-zero for errors.
+ */
+int android_logcat_run_command(android_logcat_context ctx, int output, int error,
+                               int argc, char* const* argv, char* const* envp);
+
+/* Will not block, performed in-process
+ *
+ * Starts a thread, opens a pipe, returns reading end fd, saves off argv.
+ * The command supports 2>&1 (mix content) and 2>/dev/null (drop content) for
+ * scripted error (stderr) redirection.
+ */
+int android_logcat_run_command_thread(android_logcat_context ctx, int argc,
+                                      char* const* argv, char* const* envp);
+int android_logcat_run_command_thread_running(android_logcat_context ctx);
+
+/* Finished with context
+ *
+ * Kill the command thread ASAP (if any), and free up all associated resources.
+ *
+ * Return value is the result of the android_logcat_run_command, or
+ * non-zero for any errors.
+ */
+int android_logcat_destroy(android_logcat_context* ctx);
+
+/* derived helpers */
+
+/*
+ * In-process thread that acts like somewhat like libc-like system and popen
+ * respectively.  Can not handle shell scripting, only pure calls to the
+ * logcat operations. The android_logcat_system is a wrapper for the
+ * create_android_logcat, android_logcat_run_command and android_logcat_destroy
+ * API above.  The android_logcat_popen is a wrapper for the
+ * android_logcat_run_command_thread API above.  The android_logcat_pclose is
+ * a wrapper for a reasonable wait until output has subsided for command
+ * completion, fclose on the FILE pointer and the android_logcat_destroy API.
+ */
+int android_logcat_system(const char* command);
+FILE* android_logcat_popen(android_logcat_context* ctx, const char* command);
+int android_logcat_pclose(android_logcat_context* ctx, FILE* output);
+
+#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOGCAT_H */
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 4a171fd..15cef1a 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -1,4 +1,18 @@
-// Copyright 2006-2015 The Android Open Source Project
+/*
+ * Copyright (C) 2006-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -8,8 +22,8 @@
 #include <fcntl.h>
 #include <getopt.h>
 #include <math.h>
+#include <pthread.h>
 #include <sched.h>
-#include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -22,8 +36,10 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <atomic>
 #include <memory>
 #include <string>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
@@ -31,6 +47,7 @@
 #include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
+#include <log/logcat.h>
 #include <log/logprint.h>
 #include <private/android_logger.h>
 #include <system/thread_defs.h>
@@ -39,16 +56,82 @@
 
 #define DEFAULT_MAX_ROTATED_LOGS 4
 
-static AndroidLogFormat * g_logformat;
+struct android_logcat_context_internal {
+    // status
+    volatile std::atomic_int retval;  // valid if thread_stopped set
+    // Arguments passed in, or copies and storage thereof if a thread.
+    int argc;
+    char* const* argv;
+    char* const* envp;
+    std::vector<std::string> args;
+    std::vector<const char*> argv_hold;
+    std::vector<std::string> envs;
+    std::vector<const char*> envp_hold;
+    int output_fd;
+    int error_fd;
 
-/* logd prefixes records with a length field */
+    // library
+    int fds[2];    // From popen call
+    FILE* output;  // everything writes to fileno(output), buffer unused
+    FILE* error;   // unless error == output.
+    pthread_t thr;
+    volatile std::atomic_bool stop;  // quick exit flag
+    volatile std::atomic_bool thread_stopped;
+    bool stderr_null;    // shell "2>/dev/null"
+    bool stderr_stdout;  // shell "2>&1"
+
+    // global variables
+    AndroidLogFormat* logformat;
+    const char* outputFileName;
+    // 0 means "no log rotation"
+    size_t logRotateSizeKBytes;
+    // 0 means "unbounded"
+    size_t maxRotatedLogs;
+    size_t outByteCount;
+    int printBinary;
+    int devCount;  // >1 means multiple
+    pcrecpp::RE* regex;
+    // 0 means "infinite"
+    size_t maxCount;
+    size_t printCount;
+    bool printItAnyways;
+    bool debug;
+
+    // static variables
+    bool hasOpenedEventTagMap;
+    EventTagMap* eventTagMap;
+};
+
+// Creates a context associated with this logcat instance
+android_logcat_context create_android_logcat() {
+    android_logcat_context_internal* context;
+
+    context = (android_logcat_context_internal*)calloc(
+        1, sizeof(android_logcat_context_internal));
+    if (!context) return NULL;
+
+    context->fds[0] = -1;
+    context->fds[1] = -1;
+    context->output_fd = -1;
+    context->error_fd = -1;
+    context->maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
+
+    context->argv_hold.clear();
+    context->args.clear();
+    context->envp_hold.clear();
+    context->envs.clear();
+
+    return (android_logcat_context)context;
+}
+
+// logd prefixes records with a length field
 #define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
 
 struct log_device_t {
     const char* device;
     bool binary;
-    struct logger *logger;
-    struct logger_list *logger_list;
+    struct logger* logger;
+    struct logger_list* logger_list;
     bool printed;
 
     log_device_t* next;
@@ -65,64 +148,121 @@
 
 namespace android {
 
-/* Global Variables */
-
-static const char * g_outputFileName;
-// 0 means "no log rotation"
-static size_t g_logRotateSizeKBytes;
-// 0 means "unbounded"
-static size_t g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
-static int g_outFD = -1;
-static size_t g_outByteCount;
-static int g_printBinary;
-static int g_devCount;                              // >1 means multiple
-static pcrecpp::RE* g_regex;
-// 0 means "infinite"
-static size_t g_maxCount;
-static size_t g_printCount;
-static bool g_printItAnyways;
-static bool g_debug;
-
-enum helpType {
-    HELP_FALSE,
-    HELP_TRUE,
-    HELP_FORMAT
-};
+enum helpType { HELP_FALSE, HELP_TRUE, HELP_FORMAT };
 
 // if showHelp is set, newline required in fmt statement to transition to usage
-__noreturn static void logcat_panic(enum helpType showHelp, const char *fmt, ...) __printflike(2,3);
+static void logcat_panic(android_logcat_context_internal* context,
+                         enum helpType showHelp, const char* fmt, ...)
+    __printflike(3, 4);
 
-static int openLogFile (const char *pathname)
-{
+static int openLogFile(const char* pathname) {
     return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
 }
 
-static void rotateLogs()
-{
+static void close_output(android_logcat_context_internal* context) {
+    // split output_from_error
+    if (context->error == context->output) {
+        context->output = NULL;
+        context->output_fd = -1;
+    }
+    if (context->error && (context->output_fd == fileno(context->error))) {
+        context->output_fd = -1;
+    }
+    if (context->output_fd == context->error_fd) {
+        context->output_fd = -1;
+    }
+    // close output channel
+    if (context->output) {
+        if (context->output != stdout) {
+            if (context->output_fd == fileno(context->output)) {
+                context->output_fd = -1;
+            }
+            if (context->fds[1] == fileno(context->output)) {
+                context->fds[1] = -1;
+            }
+            fclose(context->output);
+        }
+        context->output = NULL;
+    }
+    if (context->output_fd >= 0) {
+        if (context->output_fd != fileno(stdout)) {
+            if (context->fds[1] == context->output_fd) {
+                context->fds[1] = -1;
+            }
+            close(context->output_fd);
+        }
+        context->output_fd = -1;
+    }
+}
+
+static void close_error(android_logcat_context_internal* context) {
+    // split error_from_output
+    if (context->output == context->error) {
+        context->error = NULL;
+        context->error_fd = -1;
+    }
+    if (context->output && (context->error_fd == fileno(context->output))) {
+        context->error_fd = -1;
+    }
+    if (context->error_fd == context->output_fd) {
+        context->error_fd = -1;
+    }
+    // close error channel
+    if (context->error) {
+        if ((context->error != stderr) && (context->error != stdout)) {
+            if (context->error_fd == fileno(context->error)) {
+                context->error_fd = -1;
+            }
+            if (context->fds[1] == fileno(context->error)) {
+                context->fds[1] = -1;
+            }
+            fclose(context->error);
+        }
+        context->error = NULL;
+    }
+    if (context->error_fd >= 0) {
+        if ((context->error_fd != fileno(stdout)) &&
+            (context->error_fd != fileno(stderr))) {
+            if (context->fds[1] == context->error_fd) {
+                context->fds[1] = -1;
+            }
+            close(context->error_fd);
+        }
+        context->error_fd = -1;
+    }
+}
+
+static void rotateLogs(android_logcat_context_internal* context) {
     int err;
 
     // Can't rotate logs if we're not outputting to a file
-    if (g_outputFileName == NULL) {
+    if (context->outputFileName == NULL) {
         return;
     }
 
-    close(g_outFD);
+    close_output(context);
 
-    // Compute the maximum number of digits needed to count up to g_maxRotatedLogs in decimal.
-    // eg: g_maxRotatedLogs == 30 -> log10(30) == 1.477 -> maxRotationCountDigits == 2
+    // Compute the maximum number of digits needed to count up to
+    // maxRotatedLogs in decimal.  eg:
+    // maxRotatedLogs == 30
+    //   -> log10(30) == 1.477
+    //   -> maxRotationCountDigits == 2
     int maxRotationCountDigits =
-            (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
+        (context->maxRotatedLogs > 0)
+            ? (int)(floor(log10(context->maxRotatedLogs) + 1))
+            : 0;
 
-    for (int i = g_maxRotatedLogs ; i > 0 ; i--) {
+    for (int i = context->maxRotatedLogs; i > 0; i--) {
         std::string file1 = android::base::StringPrintf(
-            "%s.%.*d", g_outputFileName, maxRotationCountDigits, i);
+            "%s.%.*d", context->outputFileName, maxRotationCountDigits, i);
 
         std::string file0;
         if (i - 1 == 0) {
-            file0 = android::base::StringPrintf("%s", g_outputFileName);
+            file0 = android::base::StringPrintf("%s", context->outputFileName);
         } else {
-            file0 = android::base::StringPrintf(
-                "%s.%.*d", g_outputFileName, maxRotationCountDigits, i - 1);
+            file0 =
+                android::base::StringPrintf("%s.%.*d", context->outputFileName,
+                                            maxRotationCountDigits, i - 1);
         }
 
         if ((file0.length() == 0) || (file1.length() == 0)) {
@@ -137,153 +277,160 @@
         }
     }
 
-    g_outFD = openLogFile(g_outputFileName);
+    context->output_fd = openLogFile(context->outputFileName);
 
-    if (g_outFD < 0) {
-        logcat_panic(HELP_FALSE, "couldn't open output file");
+    if (context->output_fd < 0) {
+        logcat_panic(context, HELP_FALSE, "couldn't open output file");
+        return;
     }
+    context->output = fdopen(context->output_fd, "web");
 
-    g_outByteCount = 0;
-
+    context->outByteCount = 0;
 }
 
-void printBinary(struct log_msg *buf)
-{
+void printBinary(android_logcat_context_internal* context, struct log_msg* buf) {
     size_t size = buf->len();
 
-    TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
+    TEMP_FAILURE_RETRY(write(context->output_fd, buf, size));
 }
 
-static bool regexOk(const AndroidLogEntry& entry)
-{
-    if (!g_regex) {
+static bool regexOk(android_logcat_context_internal* context,
+                    const AndroidLogEntry& entry) {
+    if (!context->regex) {
         return true;
     }
 
     std::string messageString(entry.message, entry.messageLen);
 
-    return g_regex->PartialMatch(messageString);
+    return context->regex->PartialMatch(messageString);
 }
 
-static void processBuffer(log_device_t* dev, struct log_msg *buf)
-{
+static void processBuffer(android_logcat_context_internal* context,
+                          log_device_t* dev, struct log_msg* buf) {
     int bytesWritten = 0;
     int err;
     AndroidLogEntry entry;
     char binaryMsgBuf[1024];
 
     if (dev->binary) {
-        static bool hasOpenedEventTagMap = false;
-        static EventTagMap *eventTagMap = NULL;
-
-        if (!eventTagMap && !hasOpenedEventTagMap) {
-            eventTagMap = android_openEventTagMap(NULL);
-            hasOpenedEventTagMap = true;
+        if (!context->eventTagMap && !context->hasOpenedEventTagMap) {
+            context->eventTagMap = android_openEventTagMap(NULL);
+            context->hasOpenedEventTagMap = true;
         }
-        err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry,
-                                                 eventTagMap,
-                                                 binaryMsgBuf,
-                                                 sizeof(binaryMsgBuf));
-        //printf(">>> pri=%d len=%d msg='%s'\n",
+        err = android_log_processBinaryLogBuffer(
+            &buf->entry_v1, &entry, context->eventTagMap, binaryMsgBuf,
+            sizeof(binaryMsgBuf));
+        // printf(">>> pri=%d len=%d msg='%s'\n",
         //    entry.priority, entry.messageLen, entry.message);
     } else {
         err = android_log_processLogBuffer(&buf->entry_v1, &entry);
     }
-    if ((err < 0) && !g_debug) {
-        goto error;
+    if ((err < 0) && !context->debug) {
+        return;
     }
 
-    if (android_log_shouldPrintLine(g_logformat,
-                                    std::string(entry.tag, entry.tagLen).c_str(),
-                                    entry.priority)) {
-        bool match = regexOk(entry);
+    if (android_log_shouldPrintLine(
+            context->logformat, std::string(entry.tag, entry.tagLen).c_str(),
+            entry.priority)) {
+        bool match = regexOk(context, entry);
 
-        g_printCount += match;
-        if (match || g_printItAnyways) {
-            bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
+        context->printCount += match;
+        if (match || context->printItAnyways) {
+            bytesWritten = android_log_printLogLine(context->logformat,
+                                                    context->output_fd, &entry);
 
             if (bytesWritten < 0) {
-                logcat_panic(HELP_FALSE, "output error");
+                logcat_panic(context, HELP_FALSE, "output error");
+                return;
             }
         }
     }
 
-    g_outByteCount += bytesWritten;
+    context->outByteCount += bytesWritten;
 
-    if (g_logRotateSizeKBytes > 0
-        && (g_outByteCount / 1024) >= g_logRotateSizeKBytes
-    ) {
-        rotateLogs();
+    if (context->logRotateSizeKBytes > 0 &&
+        (context->outByteCount / 1024) >= context->logRotateSizeKBytes) {
+        rotateLogs(context);
     }
-
-error:
-    return;
 }
 
-static void maybePrintStart(log_device_t* dev, bool printDividers) {
+static void maybePrintStart(android_logcat_context_internal* context,
+                            log_device_t* dev, bool printDividers) {
     if (!dev->printed || printDividers) {
-        if (g_devCount > 1 && !g_printBinary) {
+        if (context->devCount > 1 && !context->printBinary) {
             char buf[1024];
             snprintf(buf, sizeof(buf), "--------- %s %s\n",
-                     dev->printed ? "switch to" : "beginning of",
-                     dev->device);
-            if (write(g_outFD, buf, strlen(buf)) < 0) {
-                logcat_panic(HELP_FALSE, "output error");
+                     dev->printed ? "switch to" : "beginning of", dev->device);
+            if (write(context->output_fd, buf, strlen(buf)) < 0) {
+                logcat_panic(context, HELP_FALSE, "output error");
+                return;
             }
         }
         dev->printed = true;
     }
 }
 
-static void setupOutputAndSchedulingPolicy(bool blocking) {
-    if (g_outputFileName == NULL) {
-        g_outFD = STDOUT_FILENO;
-        return;
-    }
+static void setupOutputAndSchedulingPolicy(
+    android_logcat_context_internal* context, bool blocking) {
+    if (context->outputFileName == NULL) return;
 
     if (blocking) {
         // Lower priority and set to batch scheduling if we are saving
         // the logs into files and taking continuous content.
-        if (set_sched_policy(0, SP_BACKGROUND) < 0) {
-            fprintf(stderr, "failed to set background scheduling policy\n");
+        if ((set_sched_policy(0, SP_BACKGROUND) < 0) && context->error) {
+            fprintf(context->error,
+                    "failed to set background scheduling policy\n");
         }
 
         struct sched_param param;
         memset(&param, 0, sizeof(param));
-        if (sched_setscheduler((pid_t) 0, SCHED_BATCH, &param) < 0) {
+        if (sched_setscheduler((pid_t)0, SCHED_BATCH, &param) < 0) {
             fprintf(stderr, "failed to set to batch scheduler\n");
         }
 
-        if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
-            fprintf(stderr, "failed set to priority\n");
+        if ((setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) &&
+            context->error) {
+            fprintf(context->error, "failed set to priority\n");
         }
     }
 
-    g_outFD = openLogFile (g_outputFileName);
+    close_output(context);
 
-    if (g_outFD < 0) {
-        logcat_panic(HELP_FALSE, "couldn't open output file");
+    context->output_fd = openLogFile(context->outputFileName);
+
+    if (context->output_fd < 0) {
+        logcat_panic(context, HELP_FALSE, "couldn't open output file");
+        return;
     }
 
     struct stat statbuf;
-    if (fstat(g_outFD, &statbuf) == -1) {
-        close(g_outFD);
-        logcat_panic(HELP_FALSE, "couldn't get output file stat\n");
+    if (fstat(context->output_fd, &statbuf) == -1) {
+        close_output(context);
+        logcat_panic(context, HELP_FALSE, "couldn't get output file stat\n");
+        return;
     }
 
-    if ((size_t) statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
-        close(g_outFD);
-        logcat_panic(HELP_FALSE, "invalid output file stat\n");
+    if ((size_t)statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
+        close_output(context);
+        logcat_panic(context, HELP_FALSE, "invalid output file stat\n");
+        return;
     }
 
-    g_outByteCount = statbuf.st_size;
+    context->output = fdopen(context->output_fd, "web");
+
+    context->outByteCount = statbuf.st_size;
 }
 
-static void show_help(const char *cmd)
-{
-    fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd);
+// clang-format off
+static void show_help(android_logcat_context_internal* context) {
+    if (!context->error) return;
 
-    fprintf(stderr, "options include:\n"
+    const char* cmd = strrchr(context->argv[0], '/');
+    cmd = cmd ? cmd + 1 : context->argv[0];
+
+    fprintf(context->error, "Usage: %s [options] [filterspecs]\n", cmd);
+
+    fprintf(context->error, "options include:\n"
                     "  -s              Set default filter to silent. Equivalent to filterspec '*:S'\n"
                     "  -f <file>, --file=<file>               Log to file. Default is stdout\n"
                     "  -r <kbytes>, --rotate-kbytes=<kbytes>\n"
@@ -347,7 +494,7 @@
                     "                  comes first. Improves efficiency of polling by providing\n"
                     "                  an about-to-wrap wakeup.\n");
 
-    fprintf(stderr,"\nfilterspecs are a series of \n"
+    fprintf(context->error, "\nfilterspecs are a series of \n"
                    "  <tag>[:priority]\n\n"
                    "where <tag> is a log component tag (or * for all) and priority is:\n"
                    "  V    Verbose (default for <tag>)\n"
@@ -365,9 +512,9 @@
                    "or defaults to \"threadtime\"\n\n");
 }
 
-static void show_format_help()
-{
-    fprintf(stderr,
+static void show_format_help(android_logcat_context_internal* context) {
+    if (!context->error) return;
+    fprintf(context->error,
         "-v <format>, --format=<format> options:\n"
         "  Sets log print format verb and adverbs, where <format> is:\n"
         "    brief long process raw tag thread threadtime time\n"
@@ -398,10 +545,11 @@
         "  \"<zone>\"    — Print using this public named timezone (experimental).\n\n"
     );
 }
+// clang-format on
 
-static int setLogFormat(const char * formatString)
-{
-    static AndroidLogPrintFormat format;
+static int setLogFormat(android_logcat_context_internal* context,
+                        const char* formatString) {
+    AndroidLogPrintFormat format;
 
     format = android_log_formatFromString(formatString);
 
@@ -410,42 +558,36 @@
         return -1;
     }
 
-    return android_log_setPrintFormat(g_logformat, format);
+    return android_log_setPrintFormat(context->logformat, format);
 }
 
-static const char multipliers[][2] = {
-    { "" },
-    { "K" },
-    { "M" },
-    { "G" }
-};
+static const char multipliers[][2] = { { "" }, { "K" }, { "M" }, { "G" } };
 
-static unsigned long value_of_size(unsigned long value)
-{
+static unsigned long value_of_size(unsigned long value) {
     for (unsigned i = 0;
-            (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
-            value /= 1024, ++i) ;
+         (i < sizeof(multipliers) / sizeof(multipliers[0])) && (value >= 1024);
+         value /= 1024, ++i)
+        ;
     return value;
 }
 
-static const char *multiplier_of_size(unsigned long value)
-{
+static const char* multiplier_of_size(unsigned long value) {
     unsigned i;
     for (i = 0;
-            (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
-            value /= 1024, ++i) ;
+         (i < sizeof(multipliers) / sizeof(multipliers[0])) && (value >= 1024);
+         value /= 1024, ++i)
+        ;
     return multipliers[i];
 }
 
-/*String to unsigned int, returns -1 if it fails*/
-static bool getSizeTArg(const char *ptr, size_t *val, size_t min = 0,
-                        size_t max = SIZE_MAX)
-{
+// String to unsigned int, returns -1 if it fails
+static bool getSizeTArg(const char* ptr, size_t* val, size_t min = 0,
+                        size_t max = SIZE_MAX) {
     if (!ptr) {
         return false;
     }
 
-    char *endp;
+    char* endp;
     errno = 0;
     size_t ret = (size_t)strtoll(ptr, &endp, 0);
 
@@ -461,31 +603,36 @@
     return true;
 }
 
-static void logcat_panic(enum helpType showHelp, const char *fmt, ...)
-{
-    va_list  args;
+static void logcat_panic(android_logcat_context_internal* context,
+                         enum helpType showHelp, const char* fmt, ...) {
+    context->retval = EXIT_FAILURE;
+    if (!context->error) {
+        context->stop = true;
+        return;
+    }
+
+    va_list args;
     va_start(args, fmt);
-    vfprintf(stderr, fmt,  args);
+    vfprintf(context->error, fmt, args);
     va_end(args);
 
     switch (showHelp) {
-    case HELP_TRUE:
-       show_help(getprogname());
-       break;
-    case HELP_FORMAT:
-       show_format_help();
-       break;
-    case HELP_FALSE:
-    default:
-      break;
+        case HELP_TRUE:
+            show_help(context);
+            break;
+        case HELP_FORMAT:
+            show_format_help(context);
+            break;
+        case HELP_FALSE:
+        default:
+            break;
     }
 
-    exit(EXIT_FAILURE);
+    context->stop = true;
 }
 
-static char *parseTime(log_time &t, const char *cp) {
-
-    char *ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
+static char* parseTime(log_time& t, const char* cp) {
+    char* ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
     if (ep) {
         return ep;
     }
@@ -497,14 +644,14 @@
 }
 
 // Find last logged line in <outputFileName>, or <outputFileName>.1
-static log_time lastLogTime(char *outputFileName) {
+static log_time lastLogTime(char* outputFileName) {
     log_time retval(log_time::EPOCH);
     if (!outputFileName) {
         return retval;
     }
 
     std::string directory;
-    char *file = strrchr(outputFileName, '/');
+    char* file = strrchr(outputFileName, '/');
     if (!file) {
         directory = ".";
         file = outputFileName;
@@ -515,8 +662,8 @@
         ++file;
     }
 
-    std::unique_ptr<DIR, int(*)(DIR*)>
-            dir(opendir(directory.c_str()), closedir);
+    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(directory.c_str()),
+                                            closedir);
     if (!dir.get()) {
         return retval;
     }
@@ -525,14 +672,12 @@
 
     size_t len = strlen(file);
     log_time modulo(0, NS_PER_SEC);
-    struct dirent *dp;
+    struct dirent* dp;
 
     while ((dp = readdir(dir.get())) != NULL) {
-        if ((dp->d_type != DT_REG) ||
-                (strncmp(dp->d_name, file, len) != 0) ||
-                (dp->d_name[len] &&
-                     ((dp->d_name[len] != '.') ||
-                         (strtoll(dp->d_name + 1, NULL, 10) != 1)))) {
+        if ((dp->d_type != DT_REG) || (strncmp(dp->d_name, file, len) != 0) ||
+            (dp->d_name[len] && ((dp->d_name[len] != '.') ||
+                                 (strtoll(dp->d_name + 1, NULL, 10) != 1)))) {
             continue;
         }
 
@@ -547,7 +692,7 @@
         bool found = false;
         for (const auto& line : android::base::Split(file, "\n")) {
             log_time t(log_time::EPOCH);
-            char *ep = parseTime(t, line.c_str());
+            char* ep = parseTime(t, line.c_str());
             if (!ep || (*ep != ' ')) {
                 continue;
             }
@@ -581,22 +726,30 @@
     return retval;
 }
 
-} /* namespace android */
+const char* getenv(android_logcat_context_internal* context, const char* name) {
+    if (!context->envp || !name || !*name) return NULL;
 
-void reportErrorName(const char **current,
-                     const char* name,
+    for (size_t len = strlen(name), i = 0; context->envp[i]; ++i) {
+        if (strncmp(context->envp[i], name, len)) continue;
+        if (context->envp[i][len] == '=') return &context->envp[i][len + 1];
+    }
+    return NULL;
+}
+
+}  // namespace android
+
+void reportErrorName(const char** current, const char* name,
                      bool blockSecurity) {
     if (*current) {
-       return;
+        return;
     }
     if (blockSecurity && (android_name_to_log_id(name) == LOG_ID_SECURITY)) {
-       return;
+        return;
     }
     *current = name;
 }
 
-int main(int argc, char **argv)
-{
+static int __logcat(android_logcat_context_internal* context) {
     using namespace android;
     int err;
     int hasSetLogFormat = 0;
@@ -607,27 +760,99 @@
     bool printStatistics = false;
     bool printDividers = false;
     unsigned long setLogSize = 0;
-    char *setPruneList = NULL;
-    char *setId = NULL;
+    char* setPruneList = NULL;
+    char* setId = NULL;
     int mode = ANDROID_LOG_RDONLY;
-    const char *forceFilters = NULL;
+    std::string forceFilters;
     log_device_t* devices = NULL;
     log_device_t* dev;
-    struct logger_list *logger_list;
+    struct logger_list* logger_list;
     size_t tail_lines = 0;
     log_time tail_time(log_time::EPOCH);
     size_t pid = 0;
     bool got_t = false;
 
-    signal(SIGPIPE, exit);
+    // object instantiations before goto's can happen
+    log_device_t unexpected("unexpected", false);
+    const char* openDeviceFail = NULL;
+    const char* clearFail = NULL;
+    const char* setSizeFail = NULL;
+    const char* getSizeFail = NULL;
+    int argc = context->argc;
+    char* const* argv = context->argv;
 
-    g_logformat = android_log_format_new();
+    context->output = stdout;
+    context->error = stderr;
 
-    if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
-        show_help(argv[0]);
-        return EXIT_SUCCESS;
+    for (int i = 0; i < argc; ++i) {
+        // Simulate shell stderr redirect parsing
+        if ((argv[i][0] != '2') || (argv[i][1] != '>')) continue;
+
+        size_t skip = (argv[i][2] == '>') + 2;
+        if (!strcmp(&argv[i][skip], "/dev/null")) {
+            context->stderr_null = true;
+        } else if (!strcmp(&argv[i][skip], "&1")) {
+            context->stderr_stdout = true;
+        } else {
+            // stderr file redirections are not supported
+            fprintf(context->stderr_stdout ? stdout : stderr,
+                    "stderr redirection to file %s unsupported, skipping\n",
+                    &argv[i][skip]);
+        }
     }
 
+    // Deal with setting up file descriptors and FILE pointers
+    if (context->error_fd >= 0) {
+        if (context->error_fd == context->output_fd) {
+            context->stderr_stdout = true;
+        } else if (context->stderr_null) {
+            close(context->error_fd);
+            context->error_fd = -1;
+        } else {
+            context->error = fdopen(context->error_fd, "web");
+            if (!context->error) {
+                context->retval = -errno;
+                fprintf(context->stderr_stdout ? stdout : stderr,
+                        "Failed to fdopen(error_fd=%d) %s\n", context->error_fd,
+                        strerror(errno));
+                goto exit;
+            }
+        }
+    }
+    if (context->output_fd >= 0) {
+        context->output = fdopen(context->output_fd, "web");
+        if (!context->output) {
+            context->retval = -errno;
+            fprintf(context->stderr_stdout ? stdout : context->error,
+                    "Failed to fdopen(output_fd=%d) %s\n", context->output_fd,
+                    strerror(errno));
+            goto exit;
+        }
+    }
+    if (context->stderr_stdout) context->error = context->output;
+    if (context->stderr_null) {
+        context->error_fd = -1;
+        context->error = NULL;
+    }
+    // Only happens if output=stdout
+    if ((context->output_fd < 0) && context->output) {
+        context->output_fd = fileno(context->output);
+    }
+    // Only happens if error=stdout || error=stderr
+    if ((context->error_fd < 0) && context->error) {
+        context->error_fd = fileno(context->error);
+    }
+
+    context->logformat = android_log_format_new();
+
+    if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
+        show_help(context);
+        context->retval = EXIT_SUCCESS;
+        goto exit;
+    }
+
+    // danger: getopt is _not_ reentrant
+    optind = 1;
     for (;;) {
         int ret;
 
@@ -638,6 +863,7 @@
         static const char id_str[] = "id";
         static const char wrap_str[] = "wrap";
         static const char print_str[] = "print";
+        // clang-format off
         static const struct option long_options[] = {
           { "binary",        no_argument,       NULL,   'B' },
           { "buffer",        required_argument, NULL,   'b' },
@@ -667,9 +893,11 @@
           { wrap_str,        optional_argument, NULL,   0 },
           { NULL,            0,                 NULL,   0 }
         };
+        // clang-format on
 
-        ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
-                          long_options, &option_index);
+        ret = getopt_long(argc, argv,
+                          ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
+                          &option_index);
 
         if (ret < 0) {
             break;
@@ -681,23 +909,25 @@
                 if (long_options[option_index].name == pid_str) {
                     // ToDo: determine runtime PID_MAX?
                     if (!getSizeTArg(optarg, &pid, 1)) {
-                        logcat_panic(HELP_TRUE, "%s %s out of range\n",
+                        logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
                                      long_options[option_index].name, optarg);
+                        goto exit;
                     }
                     break;
                 }
                 if (long_options[option_index].name == wrap_str) {
-                    mode |= ANDROID_LOG_WRAP |
-                            ANDROID_LOG_RDONLY |
+                    mode |= ANDROID_LOG_WRAP | ANDROID_LOG_RDONLY |
                             ANDROID_LOG_NONBLOCK;
                     // ToDo: implement API that supports setting a wrap timeout
                     size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
                     if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
-                        logcat_panic(HELP_TRUE, "%s %s out of range\n",
+                        logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
                                      long_options[option_index].name, optarg);
+                        goto exit;
                     }
-                    if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
-                        fprintf(stderr,
+                    if ((dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) &&
+                        context->error) {
+                        fprintf(context->error,
                                 "WARNING: %s %u seconds, ignoring %zu\n",
                                 long_options[option_index].name,
                                 ANDROID_LOG_WRAP_DEFAULT_TIMEOUT, dummy);
@@ -705,154 +935,163 @@
                     break;
                 }
                 if (long_options[option_index].name == print_str) {
-                    g_printItAnyways = true;
+                    context->printItAnyways = true;
                     break;
                 }
                 if (long_options[option_index].name == debug_str) {
-                    g_debug = true;
+                    context->debug = true;
                     break;
                 }
                 if (long_options[option_index].name == id_str) {
                     setId = optarg && optarg[0] ? optarg : NULL;
                     break;
                 }
-            break;
+                break;
 
             case 's':
                 // default to all silent
-                android_log_addFilterRule(g_logformat, "*:s");
-            break;
+                android_log_addFilterRule(context->logformat, "*:s");
+                break;
 
             case 'c':
                 clearLog = true;
                 mode |= ANDROID_LOG_WRONLY;
-            break;
+                break;
 
             case 'L':
-                mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK;
-            break;
+                mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE |
+                        ANDROID_LOG_NONBLOCK;
+                break;
 
             case 'd':
                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
-            break;
+                break;
 
             case 't':
                 got_t = true;
                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
-                /* FALLTHRU */
+            // FALLTHRU
             case 'T':
                 if (strspn(optarg, "0123456789") != strlen(optarg)) {
-                    char *cp = parseTime(tail_time, optarg);
+                    char* cp = parseTime(tail_time, optarg);
                     if (!cp) {
-                        logcat_panic(HELP_FALSE,
-                                     "-%c \"%s\" not in time format\n",
-                                     ret, optarg);
+                        logcat_panic(context, HELP_FALSE,
+                                     "-%c \"%s\" not in time format\n", ret,
+                                     optarg);
+                        goto exit;
                     }
                     if (*cp) {
                         char c = *cp;
                         *cp = '\0';
-                        fprintf(stderr,
+                        if (context->error) {
+                            fprintf(
+                                context->error,
                                 "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
                                 ret, optarg, c, cp + 1);
+                        }
                         *cp = c;
                     }
                 } else {
                     if (!getSizeTArg(optarg, &tail_lines, 1)) {
-                        fprintf(stderr,
-                                "WARNING: -%c %s invalid, setting to 1\n",
-                                ret, optarg);
+                        if (context->error) {
+                            fprintf(context->error,
+                                    "WARNING: -%c %s invalid, setting to 1\n",
+                                    ret, optarg);
+                        }
                         tail_lines = 1;
                     }
                 }
-            break;
+                break;
 
             case 'D':
                 printDividers = true;
-            break;
+                break;
 
             case 'e':
-                g_regex = new pcrecpp::RE(optarg);
-            break;
+                context->regex = new pcrecpp::RE(optarg);
+                break;
 
             case 'm': {
-                char *end = NULL;
-                if (!getSizeTArg(optarg, &g_maxCount)) {
-                    logcat_panic(HELP_FALSE, "-%c \"%s\" isn't an "
-                                 "integer greater than zero\n", ret, optarg);
+                char* end = NULL;
+                if (!getSizeTArg(optarg, &context->maxCount)) {
+                    logcat_panic(context, HELP_FALSE,
+                                 "-%c \"%s\" isn't an "
+                                 "integer greater than zero\n",
+                                 ret, optarg);
+                    goto exit;
                 }
-            }
-            break;
+            } break;
 
             case 'g':
                 if (!optarg) {
                     getLogSize = true;
                     break;
                 }
-                // FALLTHRU
+            // FALLTHRU
 
             case 'G': {
-                char *cp;
+                char* cp;
                 if (strtoll(optarg, &cp, 0) > 0) {
                     setLogSize = strtoll(optarg, &cp, 0);
                 } else {
                     setLogSize = 0;
                 }
 
-                switch(*cp) {
-                case 'g':
-                case 'G':
-                    setLogSize *= 1024;
-                /* FALLTHRU */
-                case 'm':
-                case 'M':
-                    setLogSize *= 1024;
-                /* FALLTHRU */
-                case 'k':
-                case 'K':
-                    setLogSize *= 1024;
-                /* FALLTHRU */
-                case '\0':
-                break;
+                switch (*cp) {
+                    case 'g':
+                    case 'G':
+                        setLogSize *= 1024;
+                    // FALLTHRU
+                    case 'm':
+                    case 'M':
+                        setLogSize *= 1024;
+                    // FALLTHRU
+                    case 'k':
+                    case 'K':
+                        setLogSize *= 1024;
+                    // FALLTHRU
+                    case '\0':
+                        break;
 
-                default:
-                    setLogSize = 0;
+                    default:
+                        setLogSize = 0;
                 }
 
                 if (!setLogSize) {
-                    fprintf(stderr, "ERROR: -G <num><multiplier>\n");
-                    return EXIT_FAILURE;
+                    logcat_panic(context, HELP_FALSE,
+                                 "ERROR: -G <num><multiplier>\n");
+                    goto exit;
                 }
-            }
-            break;
+            } break;
 
             case 'p':
                 if (!optarg) {
                     getPruneList = true;
                     break;
                 }
-                // FALLTHRU
+            // FALLTHRU
 
             case 'P':
                 setPruneList = optarg;
-            break;
+                break;
 
             case 'b': {
                 unsigned idMask = 0;
                 while ((optarg = strtok(optarg, ",:; \t\n\r\f")) != NULL) {
                     if (strcmp(optarg, "default") == 0) {
-                        idMask |= (1 << LOG_ID_MAIN) |
-                                  (1 << LOG_ID_SYSTEM) |
+                        idMask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) |
                                   (1 << LOG_ID_CRASH);
                     } else if (strcmp(optarg, "all") == 0) {
                         allSelected = true;
                         idMask = (unsigned)-1;
                     } else {
                         log_id_t log_id = android_name_to_log_id(optarg);
-                        const char *name = android_log_id_to_name(log_id);
+                        const char* name = android_log_id_to_name(log_id);
 
                         if (strcmp(name, optarg) != 0) {
-                            logcat_panic(HELP_TRUE,
+                            logcat_panic(context, HELP_TRUE,
                                          "unknown buffer %s\n", optarg);
+                            goto exit;
                         }
                         if (log_id == LOG_ID_SECURITY) allSelected = false;
                         idMask |= (1 << log_id);
@@ -861,7 +1100,7 @@
                 }
 
                 for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
-                    const char *name = android_log_id_to_name((log_id_t)i);
+                    const char* name = android_log_id_to_name((log_id_t)i);
                     log_id_t log_id = android_name_to_log_id(name);
 
                     if (log_id != (log_id_t)i) {
@@ -885,8 +1124,8 @@
                         continue;
                     }
 
-                    bool binary = !strcmp(name, "events") ||
-                                  !strcmp(name, "security");
+                    bool binary =
+                        !strcmp(name, "events") || !strcmp(name, "security");
                     log_device_t* d = new log_device_t(name, binary);
 
                     if (dev) {
@@ -895,115 +1134,105 @@
                     } else {
                         devices = dev = d;
                     }
-                    g_devCount++;
+                    context->devCount++;
                 }
-            }
-            break;
+            } break;
 
             case 'B':
-                g_printBinary = 1;
-            break;
+                context->printBinary = 1;
+                break;
 
             case 'f':
                 if ((tail_time == log_time::EPOCH) && (tail_lines == 0)) {
                     tail_time = lastLogTime(optarg);
                 }
                 // redirect output to a file
-                g_outputFileName = optarg;
-            break;
+                context->outputFileName = optarg;
+                break;
 
             case 'r':
-                if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) {
-                    logcat_panic(HELP_TRUE,
+                if (!getSizeTArg(optarg, &context->logRotateSizeKBytes, 1)) {
+                    logcat_panic(context, HELP_TRUE,
                                  "Invalid parameter \"%s\" to -r\n", optarg);
+                    goto exit;
                 }
-            break;
+                break;
 
             case 'n':
-                if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) {
-                    logcat_panic(HELP_TRUE,
+                if (!getSizeTArg(optarg, &context->maxRotatedLogs, 1)) {
+                    logcat_panic(context, HELP_TRUE,
                                  "Invalid parameter \"%s\" to -n\n", optarg);
+                    goto exit;
                 }
-            break;
+                break;
 
             case 'v':
                 if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
-                    show_format_help();
-                    exit(0);
+                    show_format_help(context);
+                    context->retval = EXIT_SUCCESS;
+                    goto exit;
                 }
-                err = setLogFormat(optarg);
+                err = setLogFormat(context, optarg);
                 if (err < 0) {
-                    logcat_panic(HELP_FORMAT,
+                    logcat_panic(context, HELP_FORMAT,
                                  "Invalid parameter \"%s\" to -v\n", optarg);
+                    goto exit;
                 }
                 hasSetLogFormat |= err;
-            break;
+                break;
 
             case 'Q':
-                /* this is a *hidden* option used to start a version of logcat                 */
-                /* in an emulated device only. it basically looks for androidboot.logcat=      */
-                /* on the kernel command line. If something is found, it extracts a log filter */
-                /* and uses it to run the program. If nothing is found, the program should     */
-                /* quit immediately                                                            */
-#define  KERNEL_OPTION  "androidboot.logcat="
-#define  CONSOLE_OPTION "androidboot.console="
+#define KERNEL_OPTION "androidboot.logcat="
+#define CONSOLE_OPTION "androidboot.console="
+                // This is a *hidden* option used to start a version of logcat
+                // in an emulated device only.  It basically looks for
+                // androidboot.logcat= on the kernel command line.  If
+                // something is found, it extracts a log filter and uses it to
+                // run the program.  If nothing is found, the program should
+                // quit immediately.
                 {
-                    int          fd;
-                    char*        logcat;
-                    char*        console;
-                    int          force_exit = 1;
-                    static char  cmdline[1024];
+                    std::string cmdline;
+                    android::base::ReadFileToString("/proc/cmdline", &cmdline);
 
-                    fd = open("/proc/cmdline", O_RDONLY);
-                    if (fd >= 0) {
-                        int  n = read(fd, cmdline, sizeof(cmdline)-1 );
-                        if (n < 0) n = 0;
-                        cmdline[n] = 0;
-                        close(fd);
-                    } else {
-                        cmdline[0] = 0;
+                    const char* logcat = strstr(cmdline.c_str(), KERNEL_OPTION);
+                    // if nothing found or invalid filters, exit quietly
+                    if (!logcat) {
+                        context->retval = EXIT_SUCCESS;
+                        goto exit;
                     }
 
-                    logcat  = strstr( cmdline, KERNEL_OPTION );
-                    console = strstr( cmdline, CONSOLE_OPTION );
-                    if (logcat != NULL) {
-                        char*  p = logcat + sizeof(KERNEL_OPTION)-1;;
-                        char*  q = strpbrk( p, " \t\n\r" );;
+                    const char* p = logcat + strlen(KERNEL_OPTION);
+                    const char* q = strpbrk(p, " \t\n\r");
+                    if (!q) q = p + strlen(p);
+                    forceFilters = std::string(p, q);
 
-                        if (q != NULL)
-                            *q = 0;
+                    // redirect our output to the emulator console
+                    const char* console =
+                        strstr(cmdline.c_str(), CONSOLE_OPTION);
+                    if (!console) break;
 
-                        forceFilters = p;
-                        force_exit   = 0;
-                    }
-                    /* if nothing found or invalid filters, exit quietly */
-                    if (force_exit) {
-                        return EXIT_SUCCESS;
+                    p = console + strlen(CONSOLE_OPTION);
+                    q = strpbrk(p, " \t\n\r");
+                    int len = q ? q - p : strlen(p);
+                    std::string devname = "/dev/" + std::string(p, len);
+                    cmdline.erase();
+
+                    if (context->error) {
+                        fprintf(context->error, "logcat using %s\n",
+                                devname.c_str());
                     }
 
-                    /* redirect our output to the emulator console */
-                    if (console) {
-                        char*  p = console + sizeof(CONSOLE_OPTION)-1;
-                        char*  q = strpbrk( p, " \t\n\r" );
-                        char   devname[64];
-                        int    len;
+                    FILE* fp = fopen(devname.c_str(), "web");
+                    devname.erase();
+                    if (!fp) break;
 
-                        if (q != NULL) {
-                            len = q - p;
-                        } else
-                            len = strlen(p);
-
-                        len = snprintf( devname, sizeof(devname), "/dev/%.*s", len, p );
-                        fprintf(stderr, "logcat using %s (%d)\n", devname, len);
-                        if (len < (int)sizeof(devname)) {
-                            fd = open( devname, O_WRONLY );
-                            if (fd >= 0) {
-                                dup2(fd, 1);
-                                dup2(fd, 2);
-                                close(fd);
-                            }
-                        }
-                    }
+                    // close output and error channels, replace with console
+                    android::close_output(context);
+                    android::close_error(context);
+                    context->stderr_stdout = true;
+                    context->output = fp;
+                    context->error = context->output;
+                    if (context->stderr_null) context->error = NULL;
                 }
                 break;
 
@@ -1012,103 +1241,119 @@
                 break;
 
             case ':':
-                logcat_panic(HELP_TRUE,
+                logcat_panic(context, HELP_TRUE,
                              "Option -%c needs an argument\n", optopt);
-                break;
+                goto exit;
 
             default:
-                logcat_panic(HELP_TRUE,
-                             "Unrecognized Option %c\n", optopt);
-                break;
+                logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n",
+                             optopt);
+                goto exit;
         }
     }
 
-    if (g_maxCount && got_t) {
-        logcat_panic(HELP_TRUE,
+    if (context->maxCount && got_t) {
+        logcat_panic(context, HELP_TRUE,
                      "Cannot use -m (--max-count) and -t together\n");
+        goto exit;
     }
-    if (g_printItAnyways && (!g_regex || !g_maxCount)) {
+    if (context->printItAnyways && (!context->regex || !context->maxCount)) {
         // One day it would be nice if --print -v color and --regex <expr>
         // could play with each other and show regex highlighted content.
-        fprintf(stderr, "WARNING: "
+        // clang-format off
+        if (context->error) {
+            fprintf(context->error, "WARNING: "
                             "--print ignored, to be used in combination with\n"
-                        "         "
+                                "         "
                             "--regex <expr> and --max-count <N>\n");
-        g_printItAnyways = false;
+        }
+        context->printItAnyways = false;
     }
 
     if (!devices) {
         dev = devices = new log_device_t("main", false);
-        g_devCount = 1;
+        context->devCount = 1;
         if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
             dev = dev->next = new log_device_t("system", false);
-            g_devCount++;
+            context->devCount++;
         }
         if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
             dev = dev->next = new log_device_t("crash", false);
-            g_devCount++;
+            context->devCount++;
         }
     }
 
-    if (g_logRotateSizeKBytes != 0 && g_outputFileName == NULL) {
-        logcat_panic(HELP_TRUE, "-r requires -f as well\n");
+    if (context->logRotateSizeKBytes != 0 && context->outputFileName == NULL) {
+        logcat_panic(context, HELP_TRUE, "-r requires -f as well\n");
+        goto exit;
     }
 
     if (setId != NULL) {
-        if (g_outputFileName == NULL) {
-            logcat_panic(HELP_TRUE, "--id='%s' requires -f as well\n", setId);
+        if (context->outputFileName == NULL) {
+            logcat_panic(context, HELP_TRUE,
+                         "--id='%s' requires -f as well\n", setId);
+            goto exit;
         }
 
-        std::string file_name = android::base::StringPrintf("%s.id", g_outputFileName);
+        std::string file_name = android::base::StringPrintf(
+                                        "%s.id", context->outputFileName);
         std::string file;
         bool file_ok = android::base::ReadFileToString(file_name, &file);
-        android::base::WriteStringToFile(setId, file_name,
-                                         S_IRUSR | S_IWUSR, getuid(), getgid());
+        android::base::WriteStringToFile(setId, file_name, S_IRUSR | S_IWUSR,
+                                         getuid(), getgid());
         if (!file_ok || (file.compare(setId) == 0)) {
             setId = NULL;
         }
     }
 
     if (hasSetLogFormat == 0) {
-        const char* logFormat = getenv("ANDROID_PRINTF_LOG");
+        const char* logFormat = android::getenv(context, "ANDROID_PRINTF_LOG");
 
         if (logFormat != NULL) {
-            err = setLogFormat(logFormat);
-            if (err < 0) {
-                fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n",
-                                    logFormat);
+            err = setLogFormat(context, logFormat);
+            if ((err < 0) && context->error) {
+                fprintf(context->error,
+                        "invalid format in ANDROID_PRINTF_LOG '%s'\n",
+                        logFormat);
             }
         } else {
-            setLogFormat("threadtime");
+            setLogFormat(context, "threadtime");
         }
     }
 
-    if (forceFilters) {
-        err = android_log_addFilterString(g_logformat, forceFilters);
+    if (forceFilters.size()) {
+        err = android_log_addFilterString(context->logformat,
+                                          forceFilters.c_str());
         if (err < 0) {
-            logcat_panic(HELP_FALSE,
+            logcat_panic(context, HELP_FALSE,
                          "Invalid filter expression in logcat args\n");
+            goto exit;
         }
     } else if (argc == optind) {
         // Add from environment variable
-        char *env_tags_orig = getenv("ANDROID_LOG_TAGS");
+        const char* env_tags_orig = android::getenv(context, "ANDROID_LOG_TAGS");
 
         if (env_tags_orig != NULL) {
-            err = android_log_addFilterString(g_logformat, env_tags_orig);
+            err = android_log_addFilterString(context->logformat,
+                                              env_tags_orig);
 
             if (err < 0) {
-                logcat_panic(HELP_TRUE,
+                logcat_panic(context, HELP_TRUE,
                             "Invalid filter expression in ANDROID_LOG_TAGS\n");
+                goto exit;
             }
         }
     } else {
         // Add from commandline
         for (int i = optind ; i < argc ; i++) {
-            err = android_log_addFilterString(g_logformat, argv[i]);
+            // skip stderr redirections of _all_ kinds
+            if ((argv[i][0] == '2') && (argv[i][1] == '>')) continue;
 
+            err = android_log_addFilterString(context->logformat, argv[i]);
             if (err < 0) {
-                logcat_panic(HELP_TRUE,
+                logcat_panic(context, HELP_TRUE,
                              "Invalid filter expression '%s'\n", argv[i]);
+                goto exit;
             }
         }
     }
@@ -1119,10 +1364,6 @@
     } else {
         logger_list = android_logger_list_alloc(mode, tail_lines, pid);
     }
-    const char *openDeviceFail = NULL;
-    const char *clearFail = NULL;
-    const char *setSizeFail = NULL;
-    const char *getSizeFail = NULL;
     // We have three orthogonal actions below to clear, set log size and
     // get log size. All sharing the same iteration loop.
     while (dev) {
@@ -1136,18 +1377,21 @@
         }
 
         if (clearLog || setId) {
-            if (g_outputFileName) {
+            if (context->outputFileName) {
                 int maxRotationCountDigits =
-                    (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
+                    (context->maxRotatedLogs > 0) ?
+                        (int)(floor(log10(context->maxRotatedLogs) + 1)) :
+                        0;
 
-                for (int i = g_maxRotatedLogs ; i >= 0 ; --i) {
+                for (int i = context->maxRotatedLogs ; i >= 0 ; --i) {
                     std::string file;
 
                     if (i == 0) {
-                        file = android::base::StringPrintf("%s", g_outputFileName);
+                        file = android::base::StringPrintf(
+                            "%s", context->outputFileName);
                     } else {
                         file = android::base::StringPrintf("%s.%.*d",
-                            g_outputFileName, maxRotationCountDigits, i);
+                            context->outputFileName, maxRotationCountDigits, i);
                     }
 
                     if (file.length() == 0) {
@@ -1181,66 +1425,79 @@
             if ((size < 0) || (readable < 0)) {
                 reportErrorName(&getSizeFail, dev->device, allSelected);
             } else {
-                printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
-                       "max entry is %db, max payload is %db\n", dev->device,
+                std::string str = android::base::StringPrintf(
+                       "%s: ring buffer is %ld%sb (%ld%sb consumed),"
+                         " max entry is %db, max payload is %db\n",
+                       dev->device,
                        value_of_size(size), multiplier_of_size(size),
                        value_of_size(readable), multiplier_of_size(readable),
-                       (int) LOGGER_ENTRY_MAX_LEN,
-                       (int) LOGGER_ENTRY_MAX_PAYLOAD);
+                       (int)LOGGER_ENTRY_MAX_LEN,
+                       (int)LOGGER_ENTRY_MAX_PAYLOAD);
+                TEMP_FAILURE_RETRY(write(context->output_fd,
+                                         str.data(), str.length()));
             }
         }
 
         dev = dev->next;
     }
+
+    context->retval = EXIT_SUCCESS;
+
     // report any errors in the above loop and exit
     if (openDeviceFail) {
-        logcat_panic(HELP_FALSE,
+        logcat_panic(context, HELP_FALSE,
                      "Unable to open log device '%s'\n", openDeviceFail);
+        goto close;
     }
     if (clearFail) {
-        logcat_panic(HELP_FALSE,
+        logcat_panic(context, HELP_FALSE,
                      "failed to clear the '%s' log\n", clearFail);
+        goto close;
     }
     if (setSizeFail) {
-        logcat_panic(HELP_FALSE,
+        logcat_panic(context, HELP_FALSE,
                      "failed to set the '%s' log size\n", setSizeFail);
+        goto close;
     }
     if (getSizeFail) {
-        logcat_panic(HELP_FALSE,
+        logcat_panic(context, HELP_FALSE,
                      "failed to get the readable '%s' log size", getSizeFail);
+        goto close;
     }
 
     if (setPruneList) {
         size_t len = strlen(setPruneList);
-        /*extra 32 bytes are needed by  android_logger_set_prune_list */
+        // extra 32 bytes are needed by android_logger_set_prune_list
         size_t bLen = len + 32;
-        char *buf = NULL;
+        char* buf = NULL;
         if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
             buf[len] = '\0';
             if (android_logger_set_prune_list(logger_list, buf, bLen)) {
-                logcat_panic(HELP_FALSE, "failed to set the prune list");
+                logcat_panic(context, HELP_FALSE,
+                             "failed to set the prune list");
             }
             free(buf);
         } else {
-            logcat_panic(HELP_FALSE, "failed to set the prune list (alloc)");
+            logcat_panic(context, HELP_FALSE,
+                         "failed to set the prune list (alloc)");
         }
+        goto close;
     }
 
     if (printStatistics || getPruneList) {
         size_t len = 8192;
-        char *buf;
+        char* buf;
 
-        for (int retry = 32;
-                (retry >= 0) && ((buf = new char [len]));
-                delete [] buf, buf = NULL, --retry) {
+        for (int retry = 32; (retry >= 0) && ((buf = new char[len]));
+             delete[] buf, buf = NULL, --retry) {
             if (getPruneList) {
                 android_logger_get_prune_list(logger_list, buf, len);
             } else {
                 android_logger_get_statistics(logger_list, buf, len);
             }
-            buf[len-1] = '\0';
+            buf[len - 1] = '\0';
             if (atol(buf) < 3) {
-                delete [] buf;
+                delete[] buf;
                 buf = NULL;
                 break;
             }
@@ -1253,11 +1510,12 @@
         }
 
         if (!buf) {
-            logcat_panic(HELP_FALSE, "failed to read data");
+            logcat_panic(context, HELP_FALSE, "failed to read data");
+            goto close;
         }
 
         // remove trailing FF
-        char *cp = buf + len - 1;
+        char* cp = buf + len - 1;
         *cp = '\0';
         bool truncated = *--cp != '\f';
         if (!truncated) {
@@ -1275,37 +1533,32 @@
             }
         }
 
-        printf("%s", cp);
-        delete [] buf;
-        return EXIT_SUCCESS;
+        len = strlen(cp);
+        TEMP_FAILURE_RETRY(write(context->output_fd, cp, len));
+        delete[] buf;
+        goto close;
     }
 
-    if (getLogSize) {
-        return EXIT_SUCCESS;
-    }
-    if (setLogSize || setPruneList) {
-        return EXIT_SUCCESS;
-    }
-    if (clearLog) {
-        return EXIT_SUCCESS;
+    if (getLogSize || setLogSize || clearLog) {
+        goto close;
     }
 
-    setupOutputAndSchedulingPolicy((mode & ANDROID_LOG_NONBLOCK) == 0);
+    setupOutputAndSchedulingPolicy(context, (mode & ANDROID_LOG_NONBLOCK) == 0);
+    if (context->stop) goto close;
 
-    //LOG_EVENT_INT(10, 12345);
-    //LOG_EVENT_LONG(11, 0x1122334455667788LL);
-    //LOG_EVENT_STRING(0, "whassup, doc?");
+    // LOG_EVENT_INT(10, 12345);
+    // LOG_EVENT_LONG(11, 0x1122334455667788LL);
+    // LOG_EVENT_STRING(0, "whassup, doc?");
 
     dev = NULL;
-    log_device_t unexpected("unexpected", false);
 
-    while (!g_maxCount || (g_printCount < g_maxCount)) {
+    while (!context->stop &&
+           (!context->maxCount || (context->printCount < context->maxCount))) {
         struct log_msg log_msg;
-        log_device_t* d;
         int ret = android_logger_list_read(logger_list, &log_msg);
-
         if (ret == 0) {
-            logcat_panic(HELP_FALSE, "read: unexpected EOF!\n");
+            logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
+            break;
         }
 
         if (ret < 0) {
@@ -1314,37 +1567,217 @@
             }
 
             if (ret == -EIO) {
-                logcat_panic(HELP_FALSE, "read: unexpected EOF!\n");
+                logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
+                break;
             }
             if (ret == -EINVAL) {
-                logcat_panic(HELP_FALSE, "read: unexpected length.\n");
+                logcat_panic(context, HELP_FALSE, "read: unexpected length.\n");
+                break;
             }
-            logcat_panic(HELP_FALSE, "logcat read failure");
+            logcat_panic(context, HELP_FALSE, "logcat read failure");
+            break;
         }
 
+        log_device_t* d;
         for (d = devices; d; d = d->next) {
             if (android_name_to_log_id(d->device) == log_msg.id()) {
                 break;
             }
         }
         if (!d) {
-            g_devCount = 2; // set to Multiple
+            context->devCount = 2; // set to Multiple
             d = &unexpected;
             d->binary = log_msg.id() == LOG_ID_EVENTS;
         }
 
         if (dev != d) {
             dev = d;
-            maybePrintStart(dev, printDividers);
+            maybePrintStart(context, dev, printDividers);
+            if (context->stop) break;
         }
-        if (g_printBinary) {
-            printBinary(&log_msg);
+        if (context->printBinary) {
+            printBinary(context, &log_msg);
         } else {
-            processBuffer(dev, &log_msg);
+            processBuffer(context, dev, &log_msg);
         }
     }
 
+close:
     android_logger_list_free(logger_list);
 
-    return EXIT_SUCCESS;
+exit:
+    // close write end of pipe to help things along
+    if (context->output_fd == context->fds[1]) {
+        android::close_output(context);
+    }
+    if (context->error_fd == context->fds[1]) {
+        android::close_error(context);
+    }
+    if (context->fds[1] >= 0) {
+        // NB: should be closed by the above
+        int save_errno = errno;
+        close(context->fds[1]);
+        errno = save_errno;
+        context->fds[1] = -1;
+    }
+    context->thread_stopped = true;
+    return context->retval;
+}
+
+// Can block
+int android_logcat_run_command(android_logcat_context ctx,
+                               int output, int error,
+                               int argc, char* const* argv,
+                               char* const* envp) {
+    android_logcat_context_internal* context = ctx;
+
+    context->output_fd = output;
+    context->error_fd = error;
+    context->argc = argc;
+    context->argv = argv;
+    context->envp = envp;
+    context->stop = false;
+    context->thread_stopped = false;
+    return __logcat(context);
+}
+
+// starts a thread, opens a pipe, returns reading end.
+int android_logcat_run_command_thread(android_logcat_context ctx,
+                                      int argc, char* const* argv,
+                                      char* const* envp) {
+    android_logcat_context_internal* context = ctx;
+
+    int save_errno = EBUSY;
+    if ((context->fds[0] >= 0) || (context->fds[1] >= 0)) {
+        goto exit;
+    }
+
+    if (pipe(context->fds) < 0) {
+        save_errno = errno;
+        goto exit;
+    }
+
+    pthread_attr_t attr;
+    if (pthread_attr_init(&attr)) {
+        save_errno = errno;
+        goto close_exit;
+    }
+
+    struct sched_param param;
+    memset(&param, 0, sizeof(param));
+    pthread_attr_setschedparam(&attr, &param);
+    pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
+    if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
+        int save_errno = errno;
+        goto pthread_attr_exit;
+    }
+
+    context->stop = false;
+    context->thread_stopped = false;
+    context->output_fd = context->fds[1];
+    // save off arguments so they remain while thread is active.
+    for (int i = 0; i < argc; ++i) {
+        context->args.push_back(std::string(argv[i]));
+    }
+    // save off environment so they remain while thread is active.
+    if (envp) for (size_t i = 0; envp[i]; ++i) {
+        context->envs.push_back(std::string(envp[i]));
+    }
+
+    for (auto& str : context->args) {
+        context->argv_hold.push_back(str.c_str());
+    }
+    context->argv_hold.push_back(NULL);
+    for (auto& str : context->envs) {
+        context->envp_hold.push_back(str.c_str());
+    }
+    context->envp_hold.push_back(NULL);
+
+    context->argc = context->argv_hold.size() - 1;
+    context->argv = (char* const*)&context->argv_hold[0];
+    context->envp = (char* const*)&context->envp_hold[0];
+
+#ifdef DEBUG
+    fprintf(stderr, "argv[%d] = {", context->argc);
+    for (auto str : context->argv_hold) {
+        fprintf(stderr, " \"%s\"", str ?: "NULL");
+    }
+    fprintf(stderr, " }\n");
+    fflush(stderr);
+#endif
+    context->retval = EXIT_SUCCESS;
+    if (pthread_create(&context->thr, &attr,
+                       (void*(*)(void*))__logcat, context)) {
+        int save_errno = errno;
+        goto argv_exit;
+    }
+    pthread_attr_destroy(&attr);
+
+    return context->fds[0];
+
+argv_exit:
+    context->argv_hold.clear();
+    context->args.clear();
+    context->envp_hold.clear();
+    context->envs.clear();
+pthread_attr_exit:
+    pthread_attr_destroy(&attr);
+close_exit:
+    close(context->fds[0]);
+    context->fds[0] = -1;
+    close(context->fds[1]);
+    context->fds[1] = -1;
+exit:
+    errno = save_errno;
+    context->stop = true;
+    context->thread_stopped = true;
+    context->retval = EXIT_FAILURE;
+    return -1;
+}
+
+// test if the thread is still doing 'stuff'
+int android_logcat_run_command_thread_running(android_logcat_context ctx) {
+    android_logcat_context_internal* context = ctx;
+
+    return context->thread_stopped == false;
+}
+
+// Finished with context
+int android_logcat_destroy(android_logcat_context* ctx) {
+    android_logcat_context_internal* context = *ctx;
+
+    *ctx = NULL;
+
+    context->stop = true;
+
+    while (context->thread_stopped == false) {
+        sched_yield();
+    }
+
+    delete context->regex;
+    context->argv_hold.clear();
+    context->args.clear();
+    context->envp_hold.clear();
+    context->envs.clear();
+    if (context->fds[0] >= 0) {
+        close(context->fds[0]);
+        context->fds[0] = -1;
+    }
+    android::close_output(context);
+    android::close_error(context);
+    if (context->fds[1] >= 0) {
+        // NB: could be closed by the above fclose(s), ignore error.
+        int save_errno = errno;
+        close(context->fds[1]);
+        errno = save_errno;
+        context->fds[1] = -1;
+    }
+
+    android_closeEventTagMap(context->eventTagMap);
+
+    int retval = context->retval;
+
+    free(context);
+
+    return retval;
 }
diff --git a/logcat/logcat_main.cpp b/logcat/logcat_main.cpp
new file mode 100644
index 0000000..9477e79
--- /dev/null
+++ b/logcat/logcat_main.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <signal.h>
+#include <stdlib.h>
+
+#include <log/logcat.h>
+
+int main(int argc, char** argv, char** envp) {
+    android_logcat_context ctx = create_android_logcat();
+    if (!ctx) return -1;
+    signal(SIGPIPE, exit);
+    int retval = android_logcat_run_command(ctx, -1, -1, argc, argv, envp);
+    int ret = android_logcat_destroy(&ctx);
+    if (!ret) ret = retval;
+    return ret;
+}
diff --git a/logcat/logcat_system.cpp b/logcat/logcat_system.cpp
new file mode 100644
index 0000000..ea393bd
--- /dev/null
+++ b/logcat/logcat_system.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <log/logcat.h>
+
+static std::string unquote(const char*& cp, const char*& delim) {
+    if ((*cp == '\'') || (*cp == '"')) {
+        // KISS: Simple quotes. Do not handle the case
+        //       of concatenation like "blah"foo'bar'
+        char quote = *cp++;
+        delim = strchr(cp, quote);
+        if (!delim) delim = cp + strlen(cp);
+        std::string str(cp, delim);
+        if (*delim) ++delim;
+        return str;
+    }
+    delim = strpbrk(cp, " \t\f\r\n");
+    if (!delim) delim = cp + strlen(cp);
+    return std::string(cp, delim);
+}
+
+static bool __android_logcat_parse(const char* command,
+                                   std::vector<std::string>& args,
+                                   std::vector<std::string>& envs) {
+    for (const char *delim, *cp = command; cp && *cp; cp = delim) {
+        while (isspace(*cp)) ++cp;
+        if ((args.size() == 0) && (*cp != '=') && !isdigit(*cp)) {
+            const char* env = cp;
+            while (isalnum(*cp) || (*cp == '_')) ++cp;
+            if (cp && (*cp == '=')) {
+                std::string str(env, ++cp);
+                str += unquote(cp, delim);
+                envs.push_back(str);
+                continue;
+            }
+            cp = env;
+        }
+        args.push_back(unquote(cp, delim));
+        if ((args.size() == 1) && (args[0] != "logcat") &&
+            (args[0] != "/system/bin/logcat")) {
+            return false;
+        }
+    }
+    return args.size() != 0;
+}
+
+FILE* android_logcat_popen(android_logcat_context* ctx, const char* command) {
+    *ctx = NULL;
+
+    std::vector<std::string> args;
+    std::vector<std::string> envs;
+    if (!__android_logcat_parse(command, args, envs)) return NULL;
+
+    std::vector<const char*> argv;
+    for (auto& str : args) {
+        argv.push_back(str.c_str());
+    }
+    argv.push_back(NULL);
+
+    std::vector<const char*> envp;
+    for (auto& str : envs) {
+        envp.push_back(str.c_str());
+    }
+    envp.push_back(NULL);
+
+    *ctx = create_android_logcat();
+    if (!*ctx) return NULL;
+
+    int fd = android_logcat_run_command_thread(
+        *ctx, argv.size() - 1, (char* const*)&argv[0], (char* const*)&envp[0]);
+    argv.clear();
+    args.clear();
+    envp.clear();
+    envs.clear();
+    if (fd < 0) {
+        android_logcat_destroy(ctx);
+        return NULL;
+    }
+
+    FILE* retval = fdopen(fd, "reb");
+    if (!retval) android_logcat_destroy(ctx);
+    return retval;
+}
+
+int android_logcat_pclose(android_logcat_context* ctx, FILE* output) {
+    if (*ctx) {
+        static const useconds_t wait_sample = 20000;
+        // Wait two seconds maximum
+        for (size_t retry = ((2 * 1000000) + wait_sample - 1) / wait_sample;
+             android_logcat_run_command_thread_running(*ctx) && retry; --retry) {
+            usleep(wait_sample);
+        }
+    }
+
+    if (output) fclose(output);
+    return android_logcat_destroy(ctx);
+}
+
+int android_logcat_system(const char* command) {
+    std::vector<std::string> args;
+    std::vector<std::string> envs;
+    if (!__android_logcat_parse(command, args, envs)) return -1;
+
+    std::vector<const char*> argv;
+    for (auto& str : args) {
+        argv.push_back(str.c_str());
+    }
+    argv.push_back(NULL);
+
+    std::vector<const char*> envp;
+    for (auto& str : envs) {
+        envp.push_back(str.c_str());
+    }
+    envp.push_back(NULL);
+
+    android_logcat_context ctx = create_android_logcat();
+    if (!ctx) return -1;
+    /* Command return value */
+    int retval = android_logcat_run_command(ctx, -1, -1, argv.size() - 1,
+                                            (char* const*)&argv[0],
+                                            (char* const*)&envp[0]);
+    /* destroy return value */
+    int ret = android_logcat_destroy(&ctx);
+    /* Paranoia merging any discrepancies between the two return values */
+    if (!ret) ret = retval;
+    return ret;
+}
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index cb8b061..1c3feba 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -48,6 +48,7 @@
 
 test_src_files := \
     logcat_test.cpp \
+    liblogcat_test.cpp \
 
 # Build tests for the device (with .so). Run with:
 #   adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
@@ -55,6 +56,6 @@
 LOCAL_MODULE := $(test_module_prefix)unit-tests
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_SHARED_LIBRARIES := liblog libbase liblogcat
 LOCAL_SRC_FILES := $(test_src_files)
 include $(BUILD_NATIVE_TEST)
diff --git a/logcat/tests/liblogcat_test.cpp b/logcat/tests/liblogcat_test.cpp
new file mode 100644
index 0000000..9e9a2c2
--- /dev/null
+++ b/logcat/tests/liblogcat_test.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <log/logcat.h>
+
+#define logcat_define(context) android_logcat_context context
+#define logcat_popen(context, command) android_logcat_popen(&context, command)
+#define logcat_pclose(context, fp) android_logcat_pclose(&context, fp)
+#define logcat_system(command) android_logcat_system(command)
+#define logcat liblogcat
+
+#include "logcat_test.cpp"
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 081bf92..9c777b3 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -33,36 +33,45 @@
 #include <log/log.h>
 #include <log/log_event_list.h>
 
+#ifndef logcat_popen
+#define logcat_define(context)
+#define logcat_popen(context, command) popen((command), "r")
+#define logcat_pclose(context, fp) pclose(fp)
+#define logcat_system(command) system(command)
+#endif
+
 #define BIG_BUFFER (5 * 1024)
 
 // enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
 // non-syscall libs. Since we are only using this in the emergency of
 // a signal to stuff a terminating code into the logs, we will spin rather
 // than try a usleep.
-#define LOG_FAILURE_RETRY(exp) ({  \
-    typeof (exp) _rc;              \
-    do {                           \
-        _rc = (exp);               \
-    } while (((_rc == -1)          \
-           && ((errno == EINTR)    \
-            || (errno == EAGAIN))) \
-          || (_rc == -EINTR)       \
-          || (_rc == -EAGAIN));    \
-    _rc; })
+#define LOG_FAILURE_RETRY(exp)                                               \
+    ({                                                                       \
+        typeof(exp) _rc;                                                     \
+        do {                                                                 \
+            _rc = (exp);                                                     \
+        } while (((_rc == -1) && ((errno == EINTR) || (errno == EAGAIN))) || \
+                 (_rc == -EINTR) || (_rc == -EAGAIN));                       \
+        _rc;                                                                 \
+    })
 
 static const char begin[] = "--------- beginning of ";
 
 TEST(logcat, buckets) {
-    FILE *fp;
+    FILE* fp;
+    logcat_define(ctx);
 
 #undef LOG_TAG
 #define LOG_TAG "inject"
     RLOGE("logcat.buckets");
     sleep(1);
 
-    ASSERT_TRUE(NULL != (fp = popen(
-      "logcat -b radio -b events -b system -b main -d 2>/dev/null",
-      "r")));
+    ASSERT_TRUE(
+        NULL !=
+        (fp = logcat_popen(
+             ctx,
+             "logcat -b radio -b events -b system -b main -d 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -71,7 +80,7 @@
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
-            while (char *cp = strrchr(buffer, '\n')) {
+            while (char* cp = strrchr(buffer, '\n')) {
                 *cp = '\0';
             }
             log_id_t id = android_name_to_log_id(buffer + sizeof(begin) - 1);
@@ -80,7 +89,7 @@
         }
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     EXPECT_EQ(15, ids);
 
@@ -88,11 +97,14 @@
 }
 
 TEST(logcat, event_tag_filter) {
-    FILE *fp;
+    FILE* fp;
+    logcat_define(ctx);
 
-    ASSERT_TRUE(NULL != (fp = popen(
-      "logcat -b events -d -s auditd am_proc_start am_pss am_proc_bound dvm_lock_sample am_wtf 2>/dev/null",
-      "r")));
+    ASSERT_TRUE(NULL !=
+                (fp = logcat_popen(ctx,
+                                   "logcat -b events -d -s auditd "
+                                   "am_proc_start am_pss am_proc_bound "
+                                   "dvm_lock_sample am_wtf 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -102,7 +114,7 @@
         ++count;
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     EXPECT_LT(4, count);
 }
@@ -115,7 +127,7 @@
     static const size_t retry = 4;
     size_t errors = retry;
     size_t num = 0;
-    for(;;) {
+    for (;;) {
         log_time ts(CLOCK_MONOTONIC);
         if (__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) >= 0) {
             if (++num >= (size_t)count) {
@@ -124,7 +136,7 @@
                 return num;
             }
             errors = retry;
-            usleep(100); // ~32 per timer tick, we are a spammer regardless
+            usleep(100);  // ~32 per timer tick, we are a spammer regardless
         } else if (--errors <= 0) {
             return num;
         }
@@ -134,22 +146,22 @@
 }
 
 TEST(logcat, year) {
-
     if (android_log_clockid() == CLOCK_MONOTONIC) {
         fprintf(stderr, "Skipping test, logd is monotonic time\n");
         return;
     }
 
     int count;
-    int tries = 3; // in case run too soon after system start or buffer clear
+    int tries = 3;  // in case run too soon after system start or buffer clear
 
     do {
-        FILE *fp;
+        FILE* fp;
+        logcat_define(ctx);
 
         char needle[32];
         time_t now;
         time(&now);
-        struct tm *ptm;
+        struct tm* ptm;
 #if !defined(_WIN32)
         struct tm tmBuf;
         ptm = localtime_r(&now, &tmBuf);
@@ -158,9 +170,10 @@
 #endif
         strftime(needle, sizeof(needle), "[ %Y-", ptm);
 
-        ASSERT_TRUE(NULL != (fp = popen(
-          "logcat -v long -v year -b all -t 3 2>/dev/null",
-          "r")));
+        ASSERT_TRUE(
+            NULL !=
+            (fp = logcat_popen(
+                 ctx, "logcat -v long -v year -b all -t 3 2>/dev/null")));
 
         char buffer[BIG_BUFFER];
 
@@ -171,7 +184,7 @@
                 ++count;
             }
         }
-        pclose(fp);
+        logcat_pclose(ctx, fp);
 
     } while ((count < 3) && --tries && inject(3 - count));
 
@@ -179,21 +192,21 @@
 }
 
 // Return a pointer to each null terminated -v long time field.
-char *fgetLongTime(char *buffer, size_t buflen, FILE *fp) {
+static char* fgetLongTime(char* buffer, size_t buflen, FILE* fp) {
     while (fgets(buffer, buflen, fp)) {
-        char *cp = buffer;
+        char* cp = buffer;
         if (*cp != '[') {
             continue;
         }
         while (*++cp == ' ') {
             ;
         }
-        char *ep = cp;
+        char* ep = cp;
         while (isdigit(*ep)) {
             ++ep;
         }
         if ((*ep != '-') && (*ep != '.')) {
-           continue;
+            continue;
         }
         // Find PID field
         while (((ep = strchr(ep, ':'))) && (*++ep != ' ')) {
@@ -211,21 +224,22 @@
 }
 
 TEST(logcat, tz) {
-
     if (android_log_clockid() == CLOCK_MONOTONIC) {
         fprintf(stderr, "Skipping test, logd is monotonic time\n");
         return;
     }
 
-    int tries = 4; // in case run too soon after system start or buffer clear
+    int tries = 4;  // in case run too soon after system start or buffer clear
     int count;
 
     do {
-        FILE *fp;
+        FILE* fp;
+        logcat_define(ctx);
 
-        ASSERT_TRUE(NULL != (fp = popen(
-          "logcat -v long -v America/Los_Angeles -b all -t 3 2>/dev/null",
-          "r")));
+        ASSERT_TRUE(NULL !=
+                    (fp = logcat_popen(ctx,
+                                       "logcat -v long -v America/Los_Angeles "
+                                       "-b all -t 3 2>/dev/null")));
 
         char buffer[BIG_BUFFER];
 
@@ -239,7 +253,7 @@
             }
         }
 
-        pclose(fp);
+        logcat_pclose(ctx, fp);
 
     } while ((count < 3) && --tries && inject(3 - count));
 
@@ -247,11 +261,13 @@
 }
 
 TEST(logcat, ntz) {
-    FILE *fp;
+    FILE* fp;
+    logcat_define(ctx);
 
-    ASSERT_TRUE(NULL != (fp = popen(
-      "logcat -v long -v America/Los_Angeles -v zone -b all -t 3 2>/dev/null",
-      "r")));
+    ASSERT_TRUE(NULL !=
+                (fp = logcat_popen(ctx,
+                                   "logcat -v long -v America/Los_Angeles -v "
+                                   "zone -b all -t 3 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -263,13 +279,13 @@
         }
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     ASSERT_EQ(0, count);
 }
 
-void do_tail(int num) {
-    int tries = 4; // in case run too soon after system start or buffer clear
+static void do_tail(int num) {
+    int tries = 4;  // in case run too soon after system start or buffer clear
     int count;
 
     if (num > 10) ++tries;
@@ -278,10 +294,11 @@
         char buffer[BIG_BUFFER];
 
         snprintf(buffer, sizeof(buffer),
-                 "logcat -v long -b all -t %d 2>/dev/null", num);
+                 "ANDROID_PRINTF_LOG=long logcat -b all -t %d 2>/dev/null", num);
 
-        FILE *fp;
-        ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+        FILE* fp;
+        logcat_define(ctx);
+        ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
 
         count = 0;
 
@@ -289,7 +306,7 @@
             ++count;
         }
 
-        pclose(fp);
+        logcat_pclose(ctx, fp);
 
     } while ((count < num) && --tries && inject(num - count));
 
@@ -313,25 +330,27 @@
 }
 
 TEST(logcat, tail_time) {
-    FILE *fp;
+    FILE* fp;
     int count;
     char buffer[BIG_BUFFER];
-    char *last_timestamp = NULL;
+    char* last_timestamp = NULL;
     // Hard to predict 100% if first (overlap) or second line will match.
     // -v nsec will in a substantial majority be the second line.
-    char *first_timestamp = NULL;
-    char *second_timestamp = NULL;
-    char *input;
+    char* first_timestamp = NULL;
+    char* second_timestamp = NULL;
+    char* input;
 
-    int tries = 4; // in case run too soon after system start or buffer clear
+    int tries = 4;  // in case run too soon after system start or buffer clear
 
     do {
-        ASSERT_TRUE(NULL != (fp = popen("logcat"
-                                        " -v long"
-                                        " -v nsec"
-                                        " -b all"
-                                        " -t 10"
-                                        " 2>&1", "r")));
+        logcat_define(ctx);
+        ASSERT_TRUE(NULL != (fp = logcat_popen(ctx,
+                                               "logcat"
+                                               " -v long"
+                                               " -v nsec"
+                                               " -b all"
+                                               " -t 10"
+                                               " 2>&1")));
         count = 0;
 
         while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
@@ -344,28 +363,30 @@
             free(last_timestamp);
             last_timestamp = strdup(input);
         }
-        pclose(fp);
+        logcat_pclose(ctx, fp);
 
     } while ((count < 10) && --tries && inject(10 - count));
 
-    EXPECT_EQ(10, count); // We want _some_ history, too small, falses below
+    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"
-                                     " -v nsec"
-                                     " -b all"
-                                     " -t '%s'"
-                                     " 2>&1",
-                                     first_timestamp);
-    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+    snprintf(buffer, sizeof(buffer),
+             "logcat"
+             " -v long"
+             " -v nsec"
+             " -b all"
+             " -t '%s'"
+             " 2>&1",
+             first_timestamp);
+    logcat_define(ctx);
+    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
 
     int second_count = 0;
     int last_timestamp_count = -1;
 
-    --count; // One less unless we match the first_timestamp
+    --count;  // One less unless we match the first_timestamp
     bool found = false;
     while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
         ++second_count;
@@ -381,9 +402,8 @@
             found = !strcmp(input, first_timestamp);
             if (found) {
                 ++count;
-                GTEST_LOG_(INFO) << "input = first("
-                                 << first_timestamp
-                                 << ")\n";
+                GTEST_LOG_(INFO)
+                    << "input = first(" << first_timestamp << ")\n";
             }
             free(first_timestamp);
             first_timestamp = NULL;
@@ -391,11 +411,8 @@
         if (second_timestamp) {
             found = found || !strcmp(input, second_timestamp);
             if (!found) {
-                GTEST_LOG_(INFO) << "input("
-                                 << input
-                                 << ") != second("
-                                 << second_timestamp
-                                 << ")\n";
+                GTEST_LOG_(INFO) << "input(" << input << ") != second("
+                                 << second_timestamp << ")\n";
             }
             free(second_timestamp);
             second_timestamp = NULL;
@@ -404,7 +421,7 @@
             last_timestamp_count = second_count;
         }
     }
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     EXPECT_TRUE(found);
     if (!found) {
@@ -436,10 +453,11 @@
 
     ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
 
-    FILE *fp;
-    ASSERT_TRUE(NULL != (fp = popen(
-      "logcat -v brief -b events -t 100 2>/dev/null",
-      "r")));
+    FILE* fp;
+    logcat_define(ctx);
+    ASSERT_TRUE(NULL !=
+                (fp = logcat_popen(
+                     ctx, "logcat -v brief -b events -t 100 2>/dev/null")));
 
     char buffer[BIG_BUFFER];
 
@@ -449,27 +467,28 @@
         int p;
         unsigned long long t;
 
-        if ((2 != sscanf(buffer, "I/[0]     ( %d): %llu", &p, &t))
-         || (p != pid)) {
+        if ((2 != sscanf(buffer, "I/[0]     ( %d): %llu", &p, &t)) ||
+            (p != pid)) {
             continue;
         }
 
-        log_time tx((const char *) &t);
+        log_time tx((const char*)&t);
         if (ts == tx) {
             ++count;
         }
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     ASSERT_EQ(1, count);
 }
 
-int get_groups(const char *cmd) {
-    FILE *fp;
+static int get_groups(const char* cmd) {
+    FILE* fp;
+    logcat_define(ctx);
 
     // NB: crash log only available in user space
-    EXPECT_TRUE(NULL != (fp = popen(cmd, "r")));
+    EXPECT_TRUE(NULL != (fp = logcat_popen(ctx, cmd)));
 
     if (fp == NULL) {
         return 0;
@@ -487,75 +506,78 @@
         size = consumed = max = payload = 0;
         // NB: crash log can be very small, not hit a Kb of consumed space
         //     doubly lucky we are not including it.
-        if (6 != sscanf(buffer, "%*s ring buffer is %d%2s (%d%2s consumed),"
-                                " max entry is %db, max payload is %db",
-                                &size, size_mult, &consumed, consumed_mult,
-                                &max, &payload)) {
+        if (6 != sscanf(buffer,
+                        "%*s ring buffer is %d%2s (%d%2s consumed),"
+                        " max entry is %db, max payload is %db",
+                        &size, size_mult, &consumed, consumed_mult, &max,
+                        &payload)) {
             fprintf(stderr, "WARNING: Parse error: %s", buffer);
             continue;
         }
         full_size = size;
-        switch(size_mult[0]) {
-        case 'G':
-            full_size *= 1024;
+        switch (size_mult[0]) {
+            case 'G':
+                full_size *= 1024;
             /* FALLTHRU */
-        case 'M':
-            full_size *= 1024;
+            case 'M':
+                full_size *= 1024;
             /* FALLTHRU */
-        case 'K':
-            full_size *= 1024;
+            case 'K':
+                full_size *= 1024;
             /* FALLTHRU */
-        case 'b':
-            break;
+            case 'b':
+                break;
         }
         full_consumed = consumed;
-        switch(consumed_mult[0]) {
-        case 'G':
-            full_consumed *= 1024;
+        switch (consumed_mult[0]) {
+            case 'G':
+                full_consumed *= 1024;
             /* FALLTHRU */
-        case 'M':
-            full_consumed *= 1024;
+            case 'M':
+                full_consumed *= 1024;
             /* FALLTHRU */
-        case 'K':
-            full_consumed *= 1024;
+            case 'K':
+                full_consumed *= 1024;
             /* FALLTHRU */
-        case 'b':
-            break;
+            case 'b':
+                break;
         }
         EXPECT_GT((full_size * 9) / 4, full_consumed);
         EXPECT_GT(full_size, max);
         EXPECT_GT(max, payload);
 
-        if ((((full_size * 9) / 4) >= full_consumed)
-         && (full_size > max)
-         && (max > payload)) {
+        if ((((full_size * 9) / 4) >= full_consumed) && (full_size > max) &&
+            (max > payload)) {
             ++count;
         }
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     return count;
 }
 
 TEST(logcat, get_size) {
-    ASSERT_EQ(4, get_groups(
-      "logcat -v brief -b radio -b events -b system -b main -g 2>/dev/null"));
+    ASSERT_EQ(4, get_groups("logcat -v brief -b radio -b events -b system -b "
+                            "main -g 2>/dev/null"));
 }
 
 // duplicate test for get_size, but use comma-separated list of buffers
 TEST(logcat, multiple_buffer) {
-    ASSERT_EQ(4, get_groups(
-      "logcat -v brief -b radio,events,system,main -g 2>/dev/null"));
+    ASSERT_EQ(
+        4, get_groups(
+               "logcat -v brief -b radio,events,system,main -g 2>/dev/null"));
 }
 
 TEST(logcat, bad_buffer) {
-    ASSERT_EQ(0, get_groups(
-      "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
+    ASSERT_EQ(
+        0,
+        get_groups(
+            "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
 }
 
-static void caught_blocking(int signum)
-{
+#ifndef logcat
+static void caught_blocking(int signum) {
     unsigned long long v = 0xDEADBEEFA55A0000ULL;
 
     v += getpid() & 0xFFFF;
@@ -565,7 +587,7 @@
 }
 
 TEST(logcat, blocking) {
-    FILE *fp;
+    FILE* fp;
     unsigned long long v = 0xDEADBEEFA55F0000ULL;
 
     pid_t pid = getpid();
@@ -576,10 +598,11 @@
 
     v &= 0xFFFFFFFFFFFAFFFFULL;
 
-    ASSERT_TRUE(NULL != (fp = popen(
-      "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
-      " logcat -v brief -b events 2>&1",
-      "r")));
+    ASSERT_TRUE(
+        NULL !=
+        (fp = popen("( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
+                    " logcat -v brief -b events 2>&1",
+                    "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -590,7 +613,6 @@
     signal(SIGALRM, caught_blocking);
     alarm(2);
     while (fgets(buffer, sizeof(buffer), fp)) {
-
         if (!strncmp(buffer, "DONE", 4)) {
             break;
         }
@@ -600,8 +622,7 @@
         int p;
         unsigned long long l;
 
-        if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l))
-         || (p != pid)) {
+        if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) || (p != pid)) {
             continue;
         }
 
@@ -624,8 +645,7 @@
     EXPECT_EQ(1, signals);
 }
 
-static void caught_blocking_tail(int signum)
-{
+static void caught_blocking_tail(int signum) {
     unsigned long long v = 0xA55ADEADBEEF0000ULL;
 
     v += getpid() & 0xFFFF;
@@ -635,7 +655,7 @@
 }
 
 TEST(logcat, blocking_tail) {
-    FILE *fp;
+    FILE* fp;
     unsigned long long v = 0xA55FDEADBEEF0000ULL;
 
     pid_t pid = getpid();
@@ -646,10 +666,11 @@
 
     v &= 0xFFFAFFFFFFFFFFFFULL;
 
-    ASSERT_TRUE(NULL != (fp = popen(
-      "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
-      " logcat -v brief -b events -T 5 2>&1",
-      "r")));
+    ASSERT_TRUE(
+        NULL !=
+        (fp = popen("( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
+                    " logcat -v brief -b events -T 5 2>&1",
+                    "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -660,7 +681,6 @@
     signal(SIGALRM, caught_blocking_tail);
     alarm(2);
     while (fgets(buffer, sizeof(buffer), fp)) {
-
         if (!strncmp(buffer, "DONE", 4)) {
             break;
         }
@@ -670,8 +690,7 @@
         int p;
         unsigned long long l;
 
-        if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l))
-         || (p != pid)) {
+        if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) || (p != pid)) {
             continue;
         }
 
@@ -695,13 +714,13 @@
 
     EXPECT_EQ(1, signals);
 }
+#endif
 
 // meant to be handed to ASSERT_FALSE / EXPECT_FALSE to expand the message
 static testing::AssertionResult IsFalse(int ret, const char* command) {
-    return ret ?
-        (testing::AssertionSuccess() <<
-            "ret=" << ret << " command=\"" << command << "\"") :
-        testing::AssertionFailure();
+    return ret ? (testing::AssertionSuccess()
+                  << "ret=" << ret << " command=\"" << command << "\"")
+               : testing::AssertionFailure();
 }
 
 TEST(logcat, logrotate) {
@@ -709,17 +728,18 @@
     char buf[sizeof(form)];
     ASSERT_TRUE(NULL != mkdtemp(strcpy(buf, form)));
 
-    static const char comm[] = "logcat -b radio -b events -b system -b main"
-                                     " -d -f %s/log.txt -n 7 -r 1";
+    static const char comm[] =
+        "logcat -b radio -b events -b system -b main"
+        " -d -f %s/log.txt -n 7 -r 1";
     char command[sizeof(buf) + sizeof(comm)];
     snprintf(command, sizeof(command), comm, buf);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = system(command), command));
+    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
     if (!ret) {
         snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf);
 
-        FILE *fp;
+        FILE* fp;
         EXPECT_TRUE(NULL != (fp = popen(command, "r")));
         if (fp) {
             char buffer[BIG_BUFFER];
@@ -731,7 +751,7 @@
                 char c;
 
                 if ((2 == sscanf(buffer, "%d log.tx%c", &num, &c)) &&
-                        (num <= 40)) {
+                    (num <= 40)) {
                     ++count;
                 } else if (strncmp(buffer, total, sizeof(total) - 1)) {
                     fprintf(stderr, "WARNING: Parse error: %s", buffer);
@@ -749,21 +769,23 @@
 }
 
 TEST(logcat, logrotate_suffix) {
-    static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+    static const char tmp_out_dir_form[] =
+        "/data/local/tmp/logcat.logrotate.XXXXXX";
     char tmp_out_dir[sizeof(tmp_out_dir_form)];
     ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
 
-    static const char logcat_cmd[] = "logcat -b radio -b events -b system -b main"
-                                     " -d -f %s/log.txt -n 10 -r 1";
+    static const char logcat_cmd[] =
+        "logcat -b radio -b events -b system -b main"
+        " -d -f %s/log.txt -n 10 -r 1";
     char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd)];
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = system(command), command));
+    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
     if (!ret) {
         snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir);
 
-        FILE *fp;
+        FILE* fp;
         EXPECT_TRUE(NULL != (fp = popen(command, "r")));
         char buffer[BIG_BUFFER];
         int log_file_count = 0;
@@ -774,21 +796,24 @@
                 strlen(rotated_log_filename_prefix);
             static const char log_filename[] = "log.txt";
 
-            if (!strncmp(buffer, rotated_log_filename_prefix, rotated_log_filename_prefix_len)) {
-              // Rotated file should have form log.txt.##
-              char* rotated_log_filename_suffix = buffer + rotated_log_filename_prefix_len;
-              char* endptr;
-              const long int suffix_value = strtol(rotated_log_filename_suffix, &endptr, 10);
-              EXPECT_EQ(rotated_log_filename_suffix + 2, endptr);
-              EXPECT_LE(suffix_value, 10);
-              EXPECT_GT(suffix_value, 0);
-              ++log_file_count;
-              continue;
+            if (!strncmp(buffer, rotated_log_filename_prefix,
+                         rotated_log_filename_prefix_len)) {
+                // Rotated file should have form log.txt.##
+                char* rotated_log_filename_suffix =
+                    buffer + rotated_log_filename_prefix_len;
+                char* endptr;
+                const long int suffix_value =
+                    strtol(rotated_log_filename_suffix, &endptr, 10);
+                EXPECT_EQ(rotated_log_filename_suffix + 2, endptr);
+                EXPECT_LE(suffix_value, 10);
+                EXPECT_GT(suffix_value, 0);
+                ++log_file_count;
+                continue;
             }
 
             if (!strncmp(buffer, log_filename, strlen(log_filename))) {
-              ++log_file_count;
-              continue;
+                ++log_file_count;
+                continue;
             }
 
             fprintf(stderr, "ERROR: Unexpected file: %s", buffer);
@@ -802,24 +827,26 @@
 }
 
 TEST(logcat, logrotate_continue) {
-    static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+    static const char tmp_out_dir_form[] =
+        "/data/local/tmp/logcat.logrotate.XXXXXX";
     char tmp_out_dir[sizeof(tmp_out_dir_form)];
     ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
 
     static const char log_filename[] = "log.txt";
-    static const char logcat_cmd[] = "logcat -b all -v nsec -d -f %s/%s -n 256 -r 1024";
+    static const char logcat_cmd[] =
+        "logcat -b all -v nsec -d -f %s/%s -n 256 -r 1024";
     static const char cleanup_cmd[] = "rm -rf %s";
     char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename)];
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = system(command), command));
+    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
     if (ret) {
         snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
         EXPECT_FALSE(IsFalse(system(command), command));
         return;
     }
-    FILE *fp;
+    FILE* fp;
     snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, log_filename);
     EXPECT_TRUE(NULL != ((fp = fopen(command, "r"))));
     if (!fp) {
@@ -827,10 +854,11 @@
         EXPECT_FALSE(IsFalse(system(command), command));
         return;
     }
-    char *line = NULL;
-    char *last_line = NULL; // this line is allowed to stutter, one-line overlap
-    char *second_last_line = NULL; // should never stutter
-    char *first_line = NULL; // help diagnose failure?
+    char* line = NULL;
+    char* last_line =
+        NULL;  // this line is allowed to stutter, one-line overlap
+    char* second_last_line = NULL;  // should never stutter
+    char* first_line = NULL;        // help diagnose failure?
     size_t len = 0;
     while (getline(&line, &len, fp) != -1) {
         if (!first_line) {
@@ -861,7 +889,7 @@
     // re-run the command, it should only add a few lines more content if it
     // continues where it left off.
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
-    EXPECT_FALSE(IsFalse(ret = system(command), command));
+    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
     if (ret) {
         snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
         EXPECT_FALSE(IsFalse(system(command), command));
@@ -869,7 +897,8 @@
         free(first_line);
         return;
     }
-    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir),
+                                                  closedir);
     EXPECT_NE(nullptr, dir);
     if (!dir) {
         snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
@@ -878,7 +907,7 @@
         free(first_line);
         return;
     }
-    struct dirent *entry;
+    struct dirent* entry;
     unsigned count = 0;
     while ((entry = readdir(dir.get()))) {
         if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
@@ -904,15 +933,15 @@
         unlink(command);
     }
     if (count > 1) {
-        char *brk = strpbrk(second_last_line, "\r\n");
+        char* brk = strpbrk(second_last_line, "\r\n");
         if (!brk) brk = second_last_line + strlen(second_last_line);
         fprintf(stderr, "\"%.*s\" occurred %u times\n",
-            (int)(brk - second_last_line), second_last_line, count);
+                (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);
+                    (int)(brk - first_line), first_line);
         }
     }
     free(second_last_line);
@@ -923,7 +952,8 @@
 }
 
 TEST(logcat, logrotate_clear) {
-    static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+    static const char tmp_out_dir_form[] =
+        "/data/local/tmp/logcat.logrotate.XXXXXX";
     char tmp_out_dir[sizeof(tmp_out_dir_form)];
     ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
 
@@ -932,28 +962,30 @@
     static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n %d -r 1";
     static const char clear_cmd[] = " -c";
     static const char cleanup_cmd[] = "rm -rf %s";
-    char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename) + sizeof(clear_cmd) + 32];
+    char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) +
+                 sizeof(log_filename) + sizeof(clear_cmd) + 32];
 
     // Run command with all data
     {
-        snprintf(command, sizeof(command) - sizeof(clear_cmd),
-                 logcat_cmd, tmp_out_dir, log_filename, num_val);
+        snprintf(command, sizeof(command) - sizeof(clear_cmd), logcat_cmd,
+                 tmp_out_dir, log_filename, num_val);
 
         int ret;
-        EXPECT_FALSE(IsFalse(ret = system(command), command));
+        EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
         if (ret) {
             snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
             EXPECT_FALSE(IsFalse(system(command), command));
             return;
         }
-        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir),
+                                                      closedir);
         EXPECT_NE(nullptr, dir);
         if (!dir) {
             snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
             EXPECT_FALSE(IsFalse(system(command), command));
             return;
         }
-        struct dirent *entry;
+        struct dirent* entry;
         unsigned count = 0;
         while ((entry = readdir(dir.get()))) {
             if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
@@ -969,21 +1001,22 @@
         strcat(command, clear_cmd);
 
         int ret;
-        EXPECT_FALSE(IsFalse(ret = system(command), command));
+        EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
         if (ret) {
             snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
             EXPECT_FALSE(system(command));
             EXPECT_FALSE(IsFalse(system(command), command));
             return;
         }
-        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir),
+                                                      closedir);
         EXPECT_NE(nullptr, dir);
         if (!dir) {
             snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
             EXPECT_FALSE(IsFalse(system(command), command));
             return;
         }
-        struct dirent *entry;
+        struct dirent* entry;
         unsigned count = 0;
         while ((entry = readdir(dir.get()))) {
             if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
@@ -999,24 +1032,25 @@
     EXPECT_FALSE(IsFalse(system(command), command));
 }
 
-static int logrotate_count_id(const char *logcat_cmd, const char *tmp_out_dir) {
-
+static int logrotate_count_id(const char* logcat_cmd, const char* tmp_out_dir) {
     static const char log_filename[] = "log.txt";
-    char command[strlen(tmp_out_dir) + strlen(logcat_cmd) + strlen(log_filename) + 32];
+    char command[strlen(tmp_out_dir) + strlen(logcat_cmd) +
+                 strlen(log_filename) + 32];
 
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
 
-    int ret;
-    EXPECT_FALSE(IsFalse(ret = system(command), command));
+    int ret = logcat_system(command);
     if (ret) {
+        fprintf(stderr, "system(\"%s\")=%d", command, ret);
         return -1;
     }
-    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
-    EXPECT_NE(nullptr, dir);
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir),
+                                                  closedir);
     if (!dir) {
+        fprintf(stderr, "opendir(\"%s\") failed", tmp_out_dir);
         return -1;
     }
-    struct dirent *entry;
+    struct dirent* entry;
     int count = 0;
     while ((entry = readdir(dir.get()))) {
         if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
@@ -1028,9 +1062,12 @@
 }
 
 TEST(logcat, logrotate_id) {
-    static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n 32 -r 1 --id=test";
-    static const char logcat_short_cmd[] = "logcat -b all -t 10 -f %s/%s -n 32 -r 1 --id=test";
-    static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+    static const char logcat_cmd[] =
+        "logcat -b all -d -f %s/%s -n 32 -r 1 --id=test";
+    static const char logcat_short_cmd[] =
+        "logcat -b all -t 10 -f %s/%s -n 32 -r 1 --id=test";
+    static const char tmp_out_dir_form[] =
+        "/data/local/tmp/logcat.logrotate.XXXXXX";
     static const char log_filename[] = "log.txt";
     char tmp_out_dir[strlen(tmp_out_dir_form) + 1];
     ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
@@ -1047,19 +1084,21 @@
     unlink(id_file);
     EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
 
-    FILE *fp = fopen(id_file, "w");
+    FILE* fp = fopen(id_file, "w");
     if (fp) {
         fprintf(fp, "not_a_test");
         fclose(fp);
     }
     if (getuid() != 0) {
-        chmod(id_file, 0); // API to preserve content even with signature change
+        chmod(id_file,
+              0);  // API to preserve content even with signature change
         ASSERT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
         chmod(id_file, 0600);
     }
 
     int new_signature;
-    EXPECT_LE(2, (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir)));
+    EXPECT_LE(
+        2, (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir)));
     EXPECT_GT(34, new_signature);
 
     static const char cleanup_cmd[] = "rm -rf %s";
@@ -1069,13 +1108,15 @@
 }
 
 TEST(logcat, logrotate_nodir) {
-    // expect logcat to error out on writing content and exit(1) for nodir
-    EXPECT_EQ(W_EXITCODE(1, 0),
-              system("logcat -b all -d"
-                     " -f /das/nein/gerfingerpoken/logcat/log.txt"
-                     " -n 256 -r 1024"));
+    // expect logcat to error out on writing content and not exit(0) for nodir
+    static const char command[] =
+        "logcat -b all -d"
+        " -f /das/nein/gerfingerpoken/logcat/log.txt"
+        " -n 256 -r 1024";
+    EXPECT_FALSE(IsFalse(0 == logcat_system(command), command));
 }
 
+#ifndef logcat
 static void caught_blocking_clear(int signum) {
     unsigned long long v = 0xDEADBEEFA55C0000ULL;
 
@@ -1086,7 +1127,7 @@
 }
 
 TEST(logcat, blocking_clear) {
-    FILE *fp;
+    FILE* fp;
     unsigned long long v = 0xDEADBEEFA55C0000ULL;
 
     pid_t pid = getpid();
@@ -1095,12 +1136,13 @@
 
     // This test is racey; an event occurs between clear and dump.
     // We accept that we will get a false positive, but never a false negative.
-    ASSERT_TRUE(NULL != (fp = popen(
-      "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
-      " logcat -b events -c 2>&1 ;"
-      " logcat -b events -g 2>&1 ;"
-      " logcat -v brief -b events 2>&1",
-      "r")));
+    ASSERT_TRUE(
+        NULL !=
+        (fp = popen("( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
+                    " logcat -b events -c 2>&1 ;"
+                    " logcat -b events -g 2>&1 ;"
+                    " logcat -v brief -b events 2>&1",
+                    "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -1112,7 +1154,6 @@
     signal(SIGALRM, caught_blocking_clear);
     alarm(2);
     while (fgets(buffer, sizeof(buffer), fp)) {
-
         if (!strncmp(buffer, "clearLog: ", 10)) {
             fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
             count = signals = 1;
@@ -1126,37 +1167,38 @@
         int size, consumed, max, payload;
         char size_mult[3], consumed_mult[3];
         size = consumed = max = payload = 0;
-        if (6 == sscanf(buffer, "events: ring buffer is %d%2s (%d%2s consumed),"
-                                " max entry is %db, max payload is %db",
-                                &size, size_mult, &consumed, consumed_mult,
-                                &max, &payload)) {
+        if (6 == sscanf(buffer,
+                        "events: ring buffer is %d%2s (%d%2s consumed),"
+                        " max entry is %db, max payload is %db",
+                        &size, size_mult, &consumed, consumed_mult, &max,
+                        &payload)) {
             long full_size = size, full_consumed = consumed;
 
-            switch(size_mult[0]) {
-            case 'G':
-                full_size *= 1024;
+            switch (size_mult[0]) {
+                case 'G':
+                    full_size *= 1024;
                 /* FALLTHRU */
-            case 'M':
-                full_size *= 1024;
+                case 'M':
+                    full_size *= 1024;
                 /* FALLTHRU */
-            case 'K':
-                full_size *= 1024;
+                case 'K':
+                    full_size *= 1024;
                 /* FALLTHRU */
-            case 'b':
-                break;
+                case 'b':
+                    break;
             }
-            switch(consumed_mult[0]) {
-            case 'G':
-                full_consumed *= 1024;
+            switch (consumed_mult[0]) {
+                case 'G':
+                    full_consumed *= 1024;
                 /* FALLTHRU */
-            case 'M':
-                full_consumed *= 1024;
+                case 'M':
+                    full_consumed *= 1024;
                 /* FALLTHRU */
-            case 'K':
-                full_consumed *= 1024;
+                case 'K':
+                    full_consumed *= 1024;
                 /* FALLTHRU */
-            case 'b':
-                break;
+                case 'b':
+                    break;
             }
             EXPECT_GT(full_size, full_consumed);
             EXPECT_GT(full_size, max);
@@ -1172,8 +1214,7 @@
         int p;
         unsigned long long l;
 
-        if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l))
-         || (p != pid)) {
+        if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) || (p != pid)) {
             continue;
         }
 
@@ -1199,11 +1240,13 @@
 
     EXPECT_EQ(1, signals);
 }
+#endif
 
-static bool get_white_black(char **list) {
-    FILE *fp;
+static bool get_white_black(char** list) {
+    FILE* fp;
+    logcat_define(ctx);
 
-    fp = popen("logcat -p 2>/dev/null", "r");
+    fp = logcat_popen(ctx, "logcat -p 2>/dev/null");
     if (fp == NULL) {
         fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
         return false;
@@ -1212,12 +1255,12 @@
     char buffer[BIG_BUFFER];
 
     while (fgets(buffer, sizeof(buffer), fp)) {
-        char *hold = *list;
-        char *buf = buffer;
+        char* hold = *list;
+        char* buf = buffer;
         while (isspace(*buf)) {
             ++buf;
         }
-        char *end = buf + strlen(buf);
+        char* end = buf + strlen(buf);
         while (isspace(*--end) && (end >= buf)) {
             *end = '\0';
         }
@@ -1231,28 +1274,29 @@
             asprintf(list, "%s", buf);
         }
     }
-    pclose(fp);
+    logcat_pclose(ctx, fp);
     return *list != NULL;
 }
 
-static bool set_white_black(const char *list) {
-    FILE *fp;
+static bool set_white_black(const char* list) {
+    FILE* fp;
+    logcat_define(ctx);
 
     char buffer[BIG_BUFFER];
 
     snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list ? list : "");
-    fp = popen(buffer, "r");
+    fp = logcat_popen(ctx, buffer);
     if (fp == NULL) {
         fprintf(stderr, "ERROR: %s\n", buffer);
         return false;
     }
 
     while (fgets(buffer, sizeof(buffer), fp)) {
-        char *buf = buffer;
+        char* buf = buffer;
         while (isspace(*buf)) {
             ++buf;
         }
-        char *end = buf + strlen(buf);
+        char* end = buf + strlen(buf);
         while ((end > buf) && isspace(*--end)) {
             *end = '\0';
         }
@@ -1260,15 +1304,15 @@
             continue;
         }
         fprintf(stderr, "%s\n", buf);
-        pclose(fp);
+        logcat_pclose(ctx, fp);
         return false;
     }
-    return pclose(fp) == 0;
+    return logcat_pclose(ctx, fp) == 0;
 }
 
 TEST(logcat, white_black_adjust) {
-    char *list = NULL;
-    char *adjust = NULL;
+    char* list = NULL;
+    char* adjust = NULL;
 
     get_white_black(&list);
 
@@ -1297,55 +1341,72 @@
 }
 
 TEST(logcat, regex) {
-    FILE *fp;
+    FILE* fp;
+    logcat_define(ctx);
     int count = 0;
 
     char buffer[BIG_BUFFER];
+// Have to make liblogcat data unique from logcat data injection
+#ifdef logcat
+#define logcat_regex_prefix "lolcat_test"
+#else
+#define logcat_regex_prefix "logcat_test"
+#endif
 
-    snprintf(buffer, sizeof(buffer), "logcat --pid %d -d -e logcat_test_a+b", getpid());
+    snprintf(buffer, sizeof(buffer),
+             "logcat --pid %d -d -e " logcat_regex_prefix "_a+b", getpid());
 
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_ab"));
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_b"));
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_aaaab"));
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_aaaa"));
-
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+                                          logcat_regex_prefix "_ab"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+                                          logcat_regex_prefix "_b"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+                                          logcat_regex_prefix "_aaaab"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+                                          logcat_regex_prefix "_aaaa"));
     // Let the logs settle
     sleep(1);
 
-    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
             continue;
         }
 
-        EXPECT_TRUE(strstr(buffer, "logcat_test_") != NULL);
+        EXPECT_TRUE(strstr(buffer, logcat_regex_prefix "_") != NULL);
 
         count++;
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     ASSERT_EQ(2, count);
 }
 
 TEST(logcat, maxcount) {
-    FILE *fp;
+    FILE* fp;
+    logcat_define(ctx);
     int count = 0;
 
     char buffer[BIG_BUFFER];
 
-    snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3", getpid());
+    snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3",
+             getpid());
 
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
-    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+    LOG_FAILURE_RETRY(
+        __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+    LOG_FAILURE_RETRY(
+        __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+    LOG_FAILURE_RETRY(
+        __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+    LOG_FAILURE_RETRY(
+        __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
 
     // Let the logs settle
     sleep(1);
 
-    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
@@ -1355,7 +1416,7 @@
         count++;
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     ASSERT_EQ(3, count);
 }
@@ -1367,8 +1428,18 @@
     ;
 
 static bool End_to_End(const char* tag, const char* fmt, ...) {
-    FILE *fp = popen("logcat -v brief -b events -v descriptive -t 100 2>/dev/null", "r");
-    if (!fp) return false;
+    logcat_define(ctx);
+    FILE* fp = logcat_popen(ctx,
+                            "logcat"
+                            " -v brief"
+                            " -b events"
+                            " -v descriptive"
+                            " -t 100"
+                            " 2>/dev/null");
+    if (!fp) {
+        fprintf(stderr, "End_to_End: popen failed");
+        return false;
+    }
 
     char buffer[BIG_BUFFER];
     va_list ap;
@@ -1377,7 +1448,7 @@
     vsnprintf(buffer, sizeof(buffer), fmt, ap);
     va_end(ap);
 
-    char *str = NULL;
+    char* str = NULL;
     asprintf(&str, "I/%s ( %%d):%%c%s%%c", tag, buffer);
     std::string expect(str);
     free(str);
@@ -1399,15 +1470,19 @@
         }
     }
 
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     if ((count == 0) && (lastMatch.length() > 0)) {
         // Help us pinpoint where things went wrong ...
         fprintf(stderr, "Closest match for\n    %s\n  is\n    %s",
                 expect.c_str(), lastMatch.c_str());
+    } else if (count > 2) {
+        fprintf(stderr, "Too many matches (%d) for %s\n", count, expect.c_str());
     }
 
-    return count == 1;
+    // Expect one the first time around as either liblogcat.descriptive or
+    // logcat.descriptive.  Expect two the second time as the other.
+    return count == 1 || count == 2;
 }
 
 TEST(logcat, descriptive) {
@@ -1422,8 +1497,8 @@
         static const char theAnswer[] = "what is five by seven";
         ctx << theAnswer;
         ctx.write();
-        EXPECT_TRUE(End_to_End(hhgtg.tagStr,
-                               "to life the universe etc=%s", theAnswer));
+        EXPECT_TRUE(
+            End_to_End(hhgtg.tagStr, "to life the universe etc=%s", theAnswer));
     }
 
     {
@@ -1434,8 +1509,7 @@
             ctx << id << (int32_t)42 << (int32_t)-1 << (int32_t)0;
             ctx.write();
             EXPECT_TRUE(End_to_End(sync.tagStr,
-                                   "[id=%s,event=42,source=-1,account=0]",
-                                   id));
+                                   "[id=%s,event=42,source=-1,account=0]", id));
         }
 
         // Partial match to description
@@ -1443,9 +1517,7 @@
             android_log_event_list ctx(sync.tagNo);
             ctx << id << (int32_t)43 << (int64_t)-1 << (int32_t)0;
             ctx.write();
-            EXPECT_TRUE(End_to_End(sync.tagStr,
-                                   "[id=%s,event=43,-1,0]",
-                                   id));
+            EXPECT_TRUE(End_to_End(sync.tagStr, "[id=%s,event=43,-1,0]", id));
         }
 
         // Negative Test of End_to_End, ensure it is working
@@ -1454,9 +1526,8 @@
             ctx << id << (int32_t)44 << (int32_t)-1 << (int64_t)0;
             ctx.write();
             fprintf(stderr, "Expect a \"Closest match\" message\n");
-            EXPECT_FALSE(End_to_End(sync.tagStr,
-                                    "[id=%s,event=44,source=-1,account=0]",
-                                    id));
+            EXPECT_FALSE(End_to_End(
+                sync.tagStr, "[id=%s,event=44,source=-1,account=0]", id));
         }
     }
 
@@ -1466,16 +1537,16 @@
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint64_t)30 << (int32_t)2;
             ctx.write();
-            EXPECT_TRUE(End_to_End(sync.tagStr,
-                                   "[aggregation time=30ms,count=2]"));
+            EXPECT_TRUE(
+                End_to_End(sync.tagStr, "[aggregation time=30ms,count=2]"));
         }
 
         {
             android_log_event_list ctx(sync.tagNo);
             ctx << (uint64_t)31570 << (int32_t)911;
             ctx.write();
-            EXPECT_TRUE(End_to_End(sync.tagStr,
-                                   "[aggregation time=31.57s,count=911]"));
+            EXPECT_TRUE(
+                End_to_End(sync.tagStr, "[aggregation time=31.57s,count=911]"));
         }
     }
 
@@ -1518,7 +1589,7 @@
 
         {
             android_log_event_list ctx(sync.tagNo);
-            ctx << (uint32_t)3221225472; // 3MB, but on purpose overflowed
+            ctx << (uint32_t)3221225472;  // 3MB, but on purpose overflowed
             ctx.write();
             EXPECT_TRUE(End_to_End(sync.tagStr, "current=-1GB"));
         }
@@ -1533,12 +1604,13 @@
 }
 
 static bool reportedSecurity(const char* command) {
-    FILE* fp = popen(command, "r");
+    logcat_define(ctx);
+    FILE* fp = logcat_popen(ctx, command);
     if (!fp) return true;
 
     std::string ret;
     bool val = android::base::ReadFdToString(fileno(fp), &ret);
-    pclose(fp);
+    logcat_pclose(ctx, fp);
 
     if (!val) return true;
     return std::string::npos != ret.find("'security'");
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 3b64f6d..0633a68 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/*              0640   system     input
+/dev/input/*              0660   root       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 1fb315c..e6def6b 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -321,7 +321,7 @@
     char idstr[80];
     struct input_id id;
 
-    fd = open(device, O_RDONLY);
+    fd = open(device, O_RDWR);
     if(fd < 0) {
         if(print_flags & PRINT_DEVICE_ERRORS)
             fprintf(stderr, "could not open %s, %s\n", device, strerror(errno));