Merge "Remove unused primes.py python file."
diff --git a/adb/adb.h b/adb/adb.h
index 6a38f18..a30e297 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -34,6 +34,8 @@
 constexpr size_t MAX_PAYLOAD_V2 = 256 * 1024;
 constexpr size_t MAX_PAYLOAD = MAX_PAYLOAD_V2;
 
+constexpr size_t LINUX_MAX_SOCKET_SIZE = 4194304;
+
 #define A_SYNC 0x434e5953
 #define A_CNXN 0x4e584e43
 #define A_OPEN 0x4e45504f
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index a35c210..ac05d4c 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -49,16 +49,23 @@
 #define MAX_PACKET_SIZE_HS 512
 #define MAX_PACKET_SIZE_SS 1024
 
-// Writes larger than 16k fail on some devices (seed with 3.10.49-g209ea2f in particular).
-#define USB_FFS_MAX_WRITE 16384
-
-// The kernel allocates a contiguous buffer for reads, which can fail for large ones due to
-// fragmentation. 16k chosen arbitrarily to match the write limit.
-#define USB_FFS_MAX_READ 16384
+// Kernels before 3.3 have a 16KiB transfer limit  That limit was replaced
+// with a 16MiB global limit in 3.3, but each URB submitted required a
+// contiguous kernel allocation, so you would get ENOMEM if you tried to
+// send something larger than the biggest available contiguous kernel
+// memory region. Large contiguous allocations could be unreliable
+// on a device kernel that has been running for a while fragmenting its
+// memory so we start with a larger allocation, and shrink the amount if
+// necessary.
+#define USB_FFS_BULK_SIZE 16384
 
 #define cpu_to_le16(x) htole16(x)
 #define cpu_to_le32(x) htole32(x)
 
+#define FUNCTIONFS_ENDPOINT_ALLOC       _IOR('g', 231, __u32)
+
+static constexpr size_t ENDPOINT_ALLOC_RETRIES = 2;
+
 static int dummy_fd = -1;
 
 struct func_desc {
@@ -173,6 +180,7 @@
     .source_comp = {
         .bLength = sizeof(ss_descriptors.source_comp),
         .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+        .bMaxBurst = 4,
     },
     .sink = {
         .bLength = sizeof(ss_descriptors.sink),
@@ -184,6 +192,7 @@
     .sink_comp = {
         .bLength = sizeof(ss_descriptors.sink_comp),
         .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+        .bMaxBurst = 4,
     },
 };
 
@@ -229,6 +238,7 @@
     ssize_t ret;
     struct desc_v1 v1_descriptor;
     struct desc_v2 v2_descriptor;
+    size_t retries = 0;
 
     v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
     v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
@@ -287,6 +297,29 @@
         goto err;
     }
 
+    h->max_rw = MAX_PAYLOAD;
+    while (h->max_rw >= USB_FFS_BULK_SIZE && retries < ENDPOINT_ALLOC_RETRIES) {
+        int ret_in = ioctl(h->bulk_in, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(h->max_rw));
+        int errno_in = errno;
+        int ret_out = ioctl(h->bulk_out, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(h->max_rw));
+        int errno_out = errno;
+
+        if (ret_in || ret_out) {
+            if (errno_in == ENODEV || errno_out == ENODEV) {
+                std::this_thread::sleep_for(100ms);
+                retries += 1;
+                continue;
+            }
+            h->max_rw /= 2;
+        } else {
+            return true;
+        }
+    }
+
+    D("[ adb: cannot call endpoint alloc: errno=%d ]", errno);
+    // Kernel pre-allocation could have failed for recoverable reasons.
+    // Continue running with a safe max rw size.
+    h->max_rw *= 2;
     return true;
 
 err:
@@ -340,7 +373,7 @@
 
     const char* buf = static_cast<const char*>(data);
     while (len > 0) {
-        int write_len = std::min(USB_FFS_MAX_WRITE, len);
+        int write_len = std::min(h->max_rw, len);
         int n = adb_write(h->bulk_in, buf, write_len);
         if (n < 0) {
             D("ERROR: fd = %d, n = %d: %s", h->bulk_in, n, strerror(errno));
@@ -359,7 +392,7 @@
 
     char* buf = static_cast<char*>(data);
     while (len > 0) {
-        int read_len = std::min(USB_FFS_MAX_READ, len);
+        int read_len = std::min(h->max_rw, len);
         int n = adb_read(h->bulk_out, buf, read_len);
         if (n < 0) {
             D("ERROR: fd = %d, n = %d: %s", h->bulk_out, n, strerror(errno));
diff --git a/adb/daemon/usb.h b/adb/daemon/usb.h
index 1d85405..55b5995 100644
--- a/adb/daemon/usb.h
+++ b/adb/daemon/usb.h
@@ -20,13 +20,6 @@
 #include <condition_variable>
 #include <mutex>
 
-// Writes larger than 16k fail on some devices (seed with 3.10.49-g209ea2f in particular).
-#define USB_FFS_MAX_WRITE 16384
-
-// The kernel allocates a contiguous buffer for reads, which can fail for large ones due to
-// fragmentation. 16k chosen arbitrarily to match the write limit.
-#define USB_FFS_MAX_READ 16384
-
 struct usb_handle {
     usb_handle() : kicked(false) {
     }
@@ -45,6 +38,8 @@
     int control = -1;
     int bulk_out = -1; /* "out" from the host's perspective => source for adbd */
     int bulk_in = -1;  /* "in" from the host's perspective => sink for adbd */
+
+    int max_rw;
 };
 
 bool init_functionfs(struct usb_handle* h);
diff --git a/adb/services.cpp b/adb/services.cpp
index df1b134..a48d855 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -242,6 +242,15 @@
     }
     D("socketpair: (%d,%d)", s[0], s[1]);
 
+#if !ADB_HOST
+    if (func == &file_sync_service) {
+        // Set file sync service socket to maximum size
+        int max_buf = LINUX_MAX_SOCKET_SIZE;
+        adb_setsockopt(s[0], SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
+        adb_setsockopt(s[1], SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
+    }
+#endif // !ADB_HOST
+
     stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
     if (sti == nullptr) {
         fatal("cannot allocate stinfo");
diff --git a/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/base/Android.bp b/base/Android.bp
index b9a6e0b..694e029 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -44,6 +44,9 @@
                 "properties.cpp",
             ],
             cppflags: ["-Wexit-time-destructors"],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
         },
         darwin: {
             srcs: ["errors_unix.cpp"],
@@ -89,6 +92,9 @@
     target: {
         android: {
             srcs: ["properties_test.cpp"],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
         },
         windows: {
             srcs: ["utf8_test.cpp"],
diff --git a/base/strings.cpp b/base/strings.cpp
index 46fe939..bfdaf12 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -36,11 +36,12 @@
 
   size_t base = 0;
   size_t found;
-  do {
+  while (true) {
     found = s.find_first_of(delimiters, base);
     result.push_back(s.substr(base, found - base));
+    if (found == s.npos) break;
     base = found + 1;
-  } while (found != s.npos);
+  }
 
   return result;
 }
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 7a65a00..7ed5b2b 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -251,3 +251,7 @@
   ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "bar"));
   ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "fool"));
 }
+
+TEST(strings, ubsan_28729303) {
+  android::base::Split("/dev/null", ":");
+}
diff --git a/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..08b0047 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;
@@ -218,7 +226,7 @@
   }
 
   if (proc_info.pid != expected_pid) {
-    LOG(FATAL) << "pid mismatch: expected " << expected_pid << ", actual " << proc_info.ppid;
+    LOG(FATAL) << "pid mismatch: expected " << expected_pid << ", actual " << proc_info.pid;
   }
 }
 
@@ -238,11 +246,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 +261,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;
@@ -284,16 +297,53 @@
   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;
   }
 
+  // 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();
   check_process(target_proc_fd, target);
 
   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 +389,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..cd45bbb 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.
@@ -189,6 +190,8 @@
     fprintf(stderr, "  readdir-NULL          pass a null pointer to readdir\n");
     fprintf(stderr, "  strlen-NULL           pass a null pointer to strlen\n");
     fprintf(stderr, "\n");
+    fprintf(stderr, "  no_new_privs          set PR_SET_NO_NEW_PRIVS and then abort\n");
+    fprintf(stderr, "\n");
     fprintf(stderr, "prefix any of the above with 'thread-' to run on a new thread\n");
     fprintf(stderr, "prefix any of the above with 'exhaustfd-' to exhaust\n");
     fprintf(stderr, "all available file descriptors before crashing.\n");
@@ -276,6 +279,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/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..680ba4b 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 & (1 << 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/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_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 94b43e4..b30417f 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -25,63 +25,61 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <cutils/properties.h>
 
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
 
-#include "bootloader.h"
-
-// Copies slot_suffix from misc into |out_suffix|. Returns 0 on
-// success, -1 on error or if there is no non-empty slot_suffix.
-static int get_active_slot_suffix_from_misc(struct fstab *fstab,
-                                            char *out_suffix,
-                                            size_t suffix_len)
+// 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,
+                                              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;
+    std::string cmdline;
+    if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
+        for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
+            std::vector<std::string> pieces = android::base::Split(entry, "=");
+            if (pieces.size() == 2) {
+                if (pieces[0] == "androidboot.slot_suffix") {
+                    strncpy(out_suffix, pieces[1].c_str(), suffix_len);
+                    return 0;
+                }
             }
         }
     }
 
-    if (misc_fd == -1) {
-        LERROR << "Error finding misc partition";
-        return -1;
+    // if we can't find slot_suffix in cmdline, check the DT
+    static constexpr char android_dt_dir[] = "/proc/device-tree/firmware/android";
+    std::string file_name = android::base::StringPrintf("%s/compatible", android_dt_dir);
+    std::string dt_value;
+    if (android::base::ReadFileToString(file_name, &dt_value)) {
+        if (!dt_value.compare("android,firmware")) {
+            LERROR << "Error finding compatible android DT node";
+            return -1;
+        }
+
+        file_name = android::base::StringPrintf("%s/%s", android_dt_dir, "slot_suffix");
+        if (!android::base::ReadFileToString(file_name, &dt_value)) {
+            LERROR << "Error finding slot_suffix in device tree";
+            return -1;
+        }
+
+        // DT entries have a terminating '\0', so 'suffix_len' is safe.
+        strncpy(out_suffix, dt_value.c_str(), suffix_len);
+        return 0;
     }
 
-    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;
+    // slot_suffix missing in kernel cmdline or device tree
+    return -1;
 }
 
-// Gets slot_suffix from either the kernel cmdline / firmware or the
-// misc partition. Sets |out_suffix| on success and returns 0. Returns
-// -1 if slot_suffix could not be determined.
-static int get_active_slot_suffix(struct fstab *fstab, char *out_suffix,
-                                  size_t suffix_len)
+// 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];
 
@@ -94,10 +92,11 @@
         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";
+    // 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;
     }
 
@@ -119,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/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index d959798..a9deed9 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -123,6 +123,7 @@
 int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab);
 int fs_mgr_is_notrim(struct fstab_rec *fstab);
 int fs_mgr_is_formattable(struct fstab_rec *fstab);
+int fs_mgr_is_slotselect(struct fstab_rec *fstab);
 int fs_mgr_is_nofail(struct fstab_rec *fstab);
 int fs_mgr_is_latemount(struct fstab_rec *fstab);
 int fs_mgr_is_quota(struct fstab_rec *fstab);
diff --git a/init/devices.cpp b/init/devices.cpp
index 6af237c..b3b808b 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -62,19 +62,7 @@
 
 extern struct selabel_handle *sehandle;
 
-static int device_fd = -1;
-
-struct uevent {
-    const char *action;
-    const char *path;
-    const char *subsystem;
-    const char *firmware;
-    const char *partition_name;
-    const char *device_name;
-    int partition_num;
-    int major;
-    int minor;
-};
+static android::base::unique_fd device_fd;
 
 struct perms_ {
     char *name;
@@ -249,11 +237,13 @@
 
     mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
 
-    if (selabel_lookup_best_match(sehandle, &secontext, path, links, mode)) {
-        PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
-        return;
+    if (sehandle) {
+        if (selabel_lookup_best_match(sehandle, &secontext, path, links, mode)) {
+            PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
+            return;
+        }
+        setfscreatecon(secontext);
     }
-    setfscreatecon(secontext);
 
     dev = makedev(major, minor);
     /* Temporarily change egid to avoid race condition setting the gid of the
@@ -264,7 +254,7 @@
     setegid(gid);
     /* If the node already exists update its SELinux label to handle cases when
      * it was created with the wrong context during coldboot procedure. */
-    if (mknod(path, mode, dev) && (errno == EEXIST)) {
+    if (mknod(path, mode, dev) && (errno == EEXIST) && secontext) {
 
         char* fcon = nullptr;
         int rc = lgetfilecon(path, &fcon);
@@ -285,8 +275,10 @@
     chown(path, uid, -1);
     setegid(AID_ROOT);
 
-    freecon(secontext);
-    setfscreatecon(NULL);
+    if (secontext) {
+        freecon(secontext);
+        setfscreatecon(NULL);
+    }
 }
 
 static void add_platform_device(const char *path)
@@ -349,6 +341,19 @@
     }
 }
 
+static void destroy_platform_devices() {
+    struct listnode* node;
+    struct listnode* n;
+    struct platform_node* bus;
+
+    list_for_each_safe(node, n, &platform_names) {
+        list_remove(node);
+        bus = node_to_item(node, struct platform_node, list);
+        free(bus->path);
+        free(bus);
+    }
+}
+
 /* Given a path that may start with a PCI device, populate the supplied buffer
  * with the PCI domain/bus number and the peripheral ID and return 0.
  * If it doesn't start with a PCI device, or there is some error, return -1 */
@@ -515,7 +520,7 @@
         return NULL;
     memset(links, 0, sizeof(char *) * 4);
 
-    LOG(INFO) << "found " << type << " device " << device;
+    LOG(VERBOSE) << "found " << type << " device " << device;
 
     snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device);
 
@@ -875,9 +880,15 @@
     }
 }
 
+static bool inline should_stop_coldboot(coldboot_action_t act)
+{
+    return (act == COLDBOOT_STOP || act == COLDBOOT_FINISH);
+}
+
 #define UEVENT_MSG_LEN  2048
 
-static inline void handle_device_fd_with(void (handle_uevent)(struct uevent*))
+static inline coldboot_action_t handle_device_fd_with(
+        std::function<coldboot_action_t(uevent* uevent)> handle_uevent)
 {
     char msg[UEVENT_MSG_LEN+2];
     int n;
@@ -890,14 +901,18 @@
 
         struct uevent uevent;
         parse_event(msg, &uevent);
-        handle_uevent(&uevent);
+        coldboot_action_t act = handle_uevent(&uevent);
+        if (should_stop_coldboot(act))
+            return act;
     }
+
+    return COLDBOOT_CONTINUE;
 }
 
-void handle_device_fd()
+coldboot_action_t handle_device_fd(coldboot_callback fn)
 {
-    handle_device_fd_with(
-        [](struct uevent *uevent) {
+    coldboot_action_t ret = handle_device_fd_with(
+        [&](uevent* uevent) -> coldboot_action_t {
             if (selinux_status_updated() > 0) {
                 struct selabel_handle *sehandle2;
                 sehandle2 = selinux_android_file_context_handle();
@@ -907,9 +922,21 @@
                 }
             }
 
-            handle_device_event(uevent);
-            handle_firmware_event(uevent);
+            // default is to always create the devices
+            coldboot_action_t act = COLDBOOT_CREATE;
+            if (fn) {
+                act = fn(uevent);
+            }
+
+            if (act == COLDBOOT_CREATE || act == COLDBOOT_STOP) {
+                handle_device_event(uevent);
+                handle_firmware_event(uevent);
+            }
+
+            return act;
         });
+
+    return ret;
 }
 
 /* Coldboot walks parts of the /sys tree and pokes the uevent files
@@ -921,21 +948,24 @@
 ** socket's buffer.
 */
 
-static void do_coldboot(DIR *d)
+static coldboot_action_t do_coldboot(DIR *d, coldboot_callback fn)
 {
     struct dirent *de;
     int dfd, fd;
+    coldboot_action_t act = COLDBOOT_CONTINUE;
 
     dfd = dirfd(d);
 
     fd = openat(dfd, "uevent", O_WRONLY);
-    if(fd >= 0) {
+    if (fd >= 0) {
         write(fd, "add\n", 4);
         close(fd);
-        handle_device_fd();
+        act = handle_device_fd(fn);
+        if (should_stop_coldboot(act))
+            return act;
     }
 
-    while((de = readdir(d))) {
+    while (!should_stop_coldboot(act) && (de = readdir(d))) {
         DIR *d2;
 
         if(de->d_type != DT_DIR || de->d_name[0] == '.')
@@ -949,85 +979,31 @@
         if(d2 == 0)
             close(fd);
         else {
-            do_coldboot(d2);
+            act = do_coldboot(d2, fn);
             closedir(d2);
         }
     }
+
+    // default is always to continue looking for uevents
+    return act;
 }
 
-static void coldboot(const char *path)
+static coldboot_action_t coldboot(const char *path, coldboot_callback fn)
 {
     std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path), closedir);
-    if(d) {
-        do_coldboot(d.get());
-    }
-}
-
-static void early_uevent_handler(struct uevent *uevent, const char *base, bool is_block)
-{
-    const char *name;
-    char devpath[DEVPATH_LEN];
-
-    if (is_block && strncmp(uevent->subsystem, "block", 5))
-        return;
-
-    name = parse_device_name(uevent, MAX_DEV_NAME);
-    if (!name) {
-        LOG(ERROR) << "Failed to parse dev name from uevent: " << uevent->action
-                   << " " << uevent->partition_name << " " << uevent->partition_num
-                   << " " << uevent->major << ":" << uevent->minor;
-        return;
+    if (d) {
+        return do_coldboot(d.get(), fn);
     }
 
-    snprintf(devpath, sizeof(devpath), "%s%s", base, name);
-    make_dir(base, 0755);
-
-    dev_t dev = makedev(uevent->major, uevent->minor);
-    mode_t mode = 0600 | (is_block ? S_IFBLK : S_IFCHR);
-    mknod(devpath, mode, dev);
+    return COLDBOOT_CONTINUE;
 }
 
-void early_create_dev(const std::string& syspath, early_device_type dev_type)
-{
-    android::base::unique_fd dfd(open(syspath.c_str(), O_RDONLY));
-    if (dfd < 0) {
-        LOG(ERROR) << "Failed to open " << syspath;
-        return;
-    }
-
-    android::base::unique_fd fd(openat(dfd, "uevent", O_WRONLY));
-    if (fd < 0) {
-        LOG(ERROR) << "Failed to open " << syspath << "/uevent";
-        return;
-    }
-
-    fcntl(device_fd, F_SETFL, O_NONBLOCK);
-
-    write(fd, "add\n", 4);
-    handle_device_fd_with(dev_type == EARLY_BLOCK_DEV ?
-        [](struct uevent *uevent) {
-            early_uevent_handler(uevent, "/dev/block/", true);
-        } :
-        [](struct uevent *uevent) {
-            early_uevent_handler(uevent, "/dev/", false);
-        });
-}
-
-int early_device_socket_open() {
-    device_fd = uevent_open_socket(256*1024, true);
-    return device_fd < 0;
-}
-
-void early_device_socket_close() {
-    close(device_fd);
-}
-
-void device_init() {
+void device_init(const char* path, coldboot_callback fn) {
     sehandle = selinux_android_file_context_handle();
     selinux_status_open(true);
 
     /* is 256K enough? udev uses 16MB! */
-    device_fd = uevent_open_socket(256*1024, true);
+    device_fd.reset(uevent_open_socket(256*1024, true));
     if (device_fd == -1) {
         return;
     }
@@ -1039,13 +1015,33 @@
     }
 
     Timer t;
-    coldboot("/sys/class");
-    coldboot("/sys/block");
-    coldboot("/sys/devices");
-    close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
+    coldboot_action_t act;
+    if (!path) {
+        act = coldboot("/sys/class", fn);
+        if (!should_stop_coldboot(act)) {
+            act = coldboot("/sys/block", fn);
+            if (!should_stop_coldboot(act)) {
+                act = coldboot("/sys/devices", fn);
+            }
+        }
+    } else {
+        act = coldboot(path, fn);
+    }
+
+    // If we have a callback, then do as it says. If no, then the default is
+    // to always create COLDBOOT_DONE file.
+    if (!fn || (act == COLDBOOT_FINISH)) {
+        close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
+    }
+
     LOG(INFO) << "Coldboot took " << t;
 }
 
+void device_close() {
+    destroy_platform_devices();
+    device_fd.reset();
+}
+
 int get_device_fd() {
     return device_fd;
 }
diff --git a/init/devices.h b/init/devices.h
index 8e9ab7d..26a064b 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -17,16 +17,37 @@
 #ifndef _INIT_DEVICES_H
 #define _INIT_DEVICES_H
 
+#include <functional>
 #include <sys/stat.h>
 
-extern void handle_device_fd();
-extern void device_init(void);
+enum coldboot_action_t {
+    // coldboot continues without creating the device for the uevent
+    COLDBOOT_CONTINUE = 0,
+    // coldboot continues after creating the device for the uevent
+    COLDBOOT_CREATE,
+    // coldboot stops after creating the device for uevent but doesn't
+    // create the COLDBOOT_DONE file
+    COLDBOOT_STOP,
+    // same as COLDBOOT_STOP, but creates the COLDBOOT_DONE file
+    COLDBOOT_FINISH
+};
 
-enum early_device_type { EARLY_BLOCK_DEV, EARLY_CHAR_DEV };
+struct uevent {
+    const char* action;
+    const char* path;
+    const char* subsystem;
+    const char* firmware;
+    const char* partition_name;
+    const char* device_name;
+    int partition_num;
+    int major;
+    int minor;
+};
 
-extern int early_device_socket_open();
-extern void early_device_socket_close();
-extern void early_create_dev(const std::string& syspath, early_device_type dev_type);
+typedef std::function<coldboot_action_t(struct uevent* uevent)> coldboot_callback;
+extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr);
+extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr);
+extern void device_close();
 
 extern int add_dev_perms(const char *name, const char *attr,
                          mode_t perm, unsigned int uid,
diff --git a/init/init.cpp b/init/init.cpp
index 43f601f..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>
@@ -495,28 +496,48 @@
     }
 }
 
-static void process_kernel_dt() {
-    static const char android_dir[] = "/proc/device-tree/firmware/android";
+static constexpr char android_dt_dir[] = "/proc/device-tree/firmware/android";
 
-    std::string file_name = StringPrintf("%s/compatible", android_dir);
+static bool is_dt_compatible() {
+    std::string dt_value;
+    std::string file_name = StringPrintf("%s/compatible", android_dt_dir);
 
-    std::string dt_file;
-    android::base::ReadFileToString(file_name, &dt_file);
-    if (!dt_file.compare("android,firmware")) {
+    android::base::ReadFileToString(file_name, &dt_value);
+    if (!dt_value.compare("android,firmware")) {
         LOG(ERROR) << "firmware/android is not compatible with 'android,firmware'";
-        return;
+        return false;
     }
 
-    std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(android_dir), closedir);
+    return true;
+}
+
+static bool is_dt_fstab_compatible() {
+    std::string dt_value;
+    std::string file_name = StringPrintf("%s/%s/compatible", android_dt_dir, "fstab");
+
+    android::base::ReadFileToString(file_name, &dt_value);
+    if (!dt_value.compare("android,fstab")) {
+        LOG(ERROR) << "firmware/android/fstab is not compatible with 'android,fstab'";
+        return false;
+    }
+
+    return true;
+}
+
+static void process_kernel_dt() {
+    if (!is_dt_compatible()) return;
+
+    std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(android_dt_dir), closedir);
     if (!dir) return;
 
+    std::string dt_file;
     struct dirent *dp;
     while ((dp = readdir(dir.get())) != NULL) {
         if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
             continue;
         }
 
-        file_name = StringPrintf("%s/%s", android_dir, dp->d_name);
+        std::string file_name = StringPrintf("%s/%s", android_dt_dir, dp->d_name);
 
         android::base::ReadFileToString(file_name, &dt_file);
         std::replace(dt_file.begin(), dt_file.end(), ',', '.');
@@ -643,102 +664,208 @@
     }
 }
 
-/* Returns a new path consisting of base_path and the file name in reference_path. */
-static std::string get_path(const std::string& base_path, const std::string& reference_path) {
-    std::string::size_type pos = reference_path.rfind('/');
-    if (pos == std::string::npos) {
-        return base_path + '/' + reference_path;
-    } else {
-        return base_path + reference_path.substr(pos);
+static std::string import_dt_fstab() {
+    std::string fstab;
+    if (!is_dt_compatible() || !is_dt_fstab_compatible()) {
+        return fstab;
     }
-}
 
-/* Imports the fstab info from cmdline. */
-static std::string import_cmdline_fstab() {
-    std::string prefix, fstab, fstab_full;
+    std::string fstabdir_name = StringPrintf("%s/fstab", android_dt_dir);
+    std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
+    if (!fstabdir) return fstab;
 
-    import_kernel_cmdline(false,
-        [&](const std::string& key, const std::string& value, bool in_qemu __attribute__((__unused__))) {
-            if (key == "android.early.prefix") {
-                prefix = value;
-            } else if (key == "android.early.fstab") {
-                fstab = value;
-            }
-        });
-    if (!fstab.empty()) {
-        // Convert "mmcblk0p09+/odm+ext4+ro+verify" to "mmcblk0p09 /odm ext4 ro verify"
-        std::replace(fstab.begin(), fstab.end(), '+', ' ');
-        for (const auto& entry : android::base::Split(fstab, "\n")) {
-            fstab_full += prefix + entry + '\n';
+    dirent* dp;
+    while ((dp = readdir(fstabdir.get())) != NULL) {
+        // skip over name and compatible
+        if (dp->d_type != DT_DIR) {
+            continue;
         }
+
+        // skip if its not 'vendor', 'odm' or 'system'
+        if (strcmp(dp->d_name, "odm") && strcmp(dp->d_name, "system") &&
+            strcmp(dp->d_name, "vendor")) {
+            continue;
+        }
+
+        // create <dev> <mnt_point>  <type>  <mnt_flags>  <fsmgr_flags>\n
+        std::vector<std::string> fstab_entry;
+        std::string file_name;
+        std::string value;
+        file_name = StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
+        if (!android::base::ReadFileToString(file_name, &value)) {
+            LOG(ERROR) << "dt_fstab: Failed to find device for partition " << dp->d_name;
+            fstab.clear();
+            break;
+        }
+        // trim the terminating '\0' out
+        value.resize(value.size() - 1);
+        fstab_entry.push_back(value);
+        fstab_entry.push_back(StringPrintf("/%s", dp->d_name));
+
+        file_name = StringPrintf("%s/%s/type", fstabdir_name.c_str(), dp->d_name);
+        if (!android::base::ReadFileToString(file_name, &value)) {
+            LOG(ERROR) << "dt_fstab: Failed to find type for partition " << dp->d_name;
+            fstab.clear();
+            break;
+        }
+        value.resize(value.size() - 1);
+        fstab_entry.push_back(value);
+
+        file_name = StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
+        if (!android::base::ReadFileToString(file_name, &value)) {
+            LOG(ERROR) << "dt_fstab: Failed to find type for partition " << dp->d_name;
+            fstab.clear();
+            break;
+        }
+        value.resize(value.size() - 1);
+        fstab_entry.push_back(value);
+
+        file_name = StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
+        if (!android::base::ReadFileToString(file_name, &value)) {
+            LOG(ERROR) << "dt_fstab: Failed to find type for partition " << dp->d_name;
+            fstab.clear();
+            break;
+        }
+        value.resize(value.size() - 1);
+        fstab_entry.push_back(value);
+
+        fstab += android::base::Join(fstab_entry, " ");
+        fstab += '\n';
     }
-    return fstab_full;
+
+    return fstab;
 }
 
-/* Early mount vendor and ODM partitions. The fstab info is read from kernel cmdline. */
-static void early_mount() {
-    std::string fstab_string = import_cmdline_fstab();
-    if (fstab_string.empty()) {
-        LOG(INFO) << "Failed to load vendor fstab from kernel cmdline";
-        return;
+/* Early mount vendor and ODM partitions. The fstab is read from device-tree. */
+static bool early_mount() {
+    std::string fstab = import_dt_fstab();
+    if (fstab.empty()) {
+        LOG(INFO) << "Early mount skipped (missing fstab in device tree)";
+        return true;
     }
-    FILE *fstab_file = fmemopen((void *)fstab_string.c_str(), fstab_string.length(), "r");
+
+    std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
+        fmemopen(static_cast<void*>(const_cast<char*>(fstab.c_str())), fstab.length(), "r"), fclose);
     if (!fstab_file) {
-        PLOG(ERROR) << "Failed to open fstab string as FILE";
-        return;
-    }
-    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_file(fstab_file), fs_mgr_free_fstab);
-    fclose(fstab_file);
-    if (!fstab) {
-        LOG(ERROR) << "Failed to parse fstab string: " << fstab_string;
-        return;
-    }
-    LOG(INFO) << "Loaded vendor fstab from cmdline";
-
-    if (early_device_socket_open()) {
-        LOG(ERROR) << "Failed to open device uevent socket";
-        return;
+        PLOG(ERROR) << "Early mount failed to open fstab file in memory";
+        return false;
     }
 
-    /* Create /dev/device-mapper for dm-verity */
-    early_create_dev("/sys/devices/virtual/misc/device-mapper", EARLY_CHAR_DEV);
+    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> tab(
+        fs_mgr_read_fstab_file(fstab_file.get()), fs_mgr_free_fstab);
+    if (!tab) {
+        LOG(ERROR) << "Early mount fsmgr failed to load fstab from kernel:" << std::endl << fstab;
+        return false;
+    }
 
-    for (int i = 0; i < fstab->num_entries; ++i) {
-        struct fstab_rec *rec = &fstab->recs[i];
-        std::string mount_point = rec->mount_point;
-        std::string syspath = rec->blk_device;
+    // find out fstab records for odm, system and vendor
+    fstab_rec* odm_rec = fs_mgr_get_entry_for_mount_point(tab.get(), "/odm");
+    fstab_rec* system_rec = fs_mgr_get_entry_for_mount_point(tab.get(), "/system");
+    fstab_rec* vendor_rec = fs_mgr_get_entry_for_mount_point(tab.get(), "/vendor");
+    if (!odm_rec && !system_rec && !vendor_rec) {
+        // nothing to early mount
+        return true;
+    }
 
-        if (mount_point != "/vendor" && mount_point != "/odm")
-            continue;
+    // assume A/B device if we find 'slotselect' in any fstab entry
+    bool is_ab = ((odm_rec && fs_mgr_is_slotselect(odm_rec)) ||
+                  (system_rec && fs_mgr_is_slotselect(system_rec)) ||
+                  (vendor_rec && fs_mgr_is_slotselect(vendor_rec)));
+    bool found_odm = !odm_rec;
+    bool found_system = !system_rec;
+    bool found_vendor = !vendor_rec;
+    int count_odm = 0, count_vendor = 0, count_system = 0;
 
-        /* Create mount target under /dev/block/ from sysfs via uevent */
-        LOG(INFO) << "Mounting " << mount_point << " from " << syspath << "...";
-        char *devpath = strdup(get_path("/dev/block", syspath).c_str());
-        if (!devpath) {
-            PLOG(ERROR) << "Failed to strdup dev path in early mount " << syspath;
-            continue;
-        }
-        rec->blk_device = devpath;
-        early_create_dev(syspath, EARLY_BLOCK_DEV);
-
-        int rc = fs_mgr_early_setup_verity(rec);
-        if (rc == FS_MGR_EARLY_SETUP_VERITY_SUCCESS) {
-            /* Mount target is changed to /dev/block/dm-<n>; initiate its creation from sysfs counterpart */
-            early_create_dev(get_path("/sys/devices/virtual/block", rec->blk_device), EARLY_BLOCK_DEV);
-        } else if (rc == FS_MGR_EARLY_SETUP_VERITY_FAIL) {
-            LOG(ERROR) << "Failed to set up dm-verity on " << rec->blk_device;
-            continue;
-        } else { /* FS_MGR_EARLY_SETUP_VERITY_NO_VERITY */
-            LOG(INFO) << "dm-verity disabled on debuggable device; mount directly on " << rec->blk_device;
+    // create the devices we need..
+    device_init(nullptr, [&](uevent* uevent) -> coldboot_action_t {
+        if (!strncmp(uevent->subsystem, "firmware", 8)) {
+            return COLDBOOT_CONTINUE;
         }
 
-        mkdir(mount_point.c_str(), 0755);
-        rc = mount(rec->blk_device, mount_point.c_str(), rec->fs_type, rec->flags, rec->fs_options);
-        if (rc) {
-            PLOG(ERROR) << "Failed to mount on " << rec->blk_device;
+        // we need platform devices to create symlinks
+        if (!strncmp(uevent->subsystem, "platform", 8)) {
+            return COLDBOOT_CREATE;
         }
+
+        // Ignore everything that is not a block device
+        if (strncmp(uevent->subsystem, "block", 5)) {
+            return COLDBOOT_CONTINUE;
+        }
+
+        coldboot_action_t ret;
+        bool create_this_node = false;
+        if (uevent->partition_name) {
+            // prefix match partition names so we create device nodes for
+            // A/B-ed partitions
+            if (!found_odm && !strncmp(uevent->partition_name, "odm", 3)) {
+                LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name << ") partition";
+
+                // wait twice for A/B-ed partitions
+                count_odm++;
+                if (!is_ab) {
+                    found_odm = true;
+                } else if (count_odm == 2) {
+                    found_odm = true;
+                }
+
+                create_this_node = true;
+            } else if (!found_system && !strncmp(uevent->partition_name, "system", 6)) {
+                LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name << ") partition";
+
+                count_system++;
+                if (!is_ab) {
+                    found_system = true;
+                } else if (count_system == 2) {
+                    found_system = true;
+                }
+
+                create_this_node = true;
+            } else if (!found_vendor && !strncmp(uevent->partition_name, "vendor", 6)) {
+                LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name << ") partition";
+                count_vendor++;
+                if (!is_ab) {
+                    found_vendor = true;
+                } else if (count_vendor == 2) {
+                    found_vendor = true;
+                }
+
+                create_this_node = true;
+            }
+        }
+
+        // if we found all other partitions already, create this
+        // node and stop coldboot. If this is a prefix matched
+        // partition, create device node and continue. For everything
+        // else skip the device node
+        if (found_odm && found_system && found_vendor) {
+            ret = COLDBOOT_STOP;
+        } else if (create_this_node) {
+            ret = COLDBOOT_CREATE;
+        } else {
+            ret = COLDBOOT_CONTINUE;
+        }
+
+        return ret;
+    });
+
+    // TODO: add support to mount partitions w/ verity
+
+    int ret = 0;
+    if (odm_rec &&
+        (ret = fs_mgr_do_mount(tab.get(), odm_rec->mount_point, odm_rec->blk_device, NULL))) {
+        PLOG(ERROR) << "early_mount: fs_mgr_do_mount returned error for mounting odm";
+        return false;
     }
-    early_device_socket_close();
+
+    if (vendor_rec &&
+        (ret = fs_mgr_do_mount(tab.get(), vendor_rec->mount_point, vendor_rec->blk_device, NULL))) {
+        PLOG(ERROR) << "early_mount: fs_mgr_do_mount returned error for mounting vendor";
+        return false;
+    }
+
+    device_close();
+
+    return true;
 }
 
 int main(int argc, char** argv) {
@@ -787,8 +914,10 @@
     LOG(INFO) << "init " << (is_first_stage ? "first" : "second") << " stage started!";
 
     if (is_first_stage) {
-        // Mount devices defined in android.early.* kernel commandline
-        early_mount();
+        if (!early_mount()) {
+            LOG(ERROR) << "Failed to mount required partitions early ...";
+            panic();
+        }
 
         // Set up SELinux, loading the SELinux policy.
         selinux_initialize(true);
@@ -831,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/liblog/logd_reader.c b/liblog/logd_reader.c
index 9411f36..ccc7da8 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_MAX || logId == LOG_ID_KERNEL) {
+    if (logId > LOG_ID_KERNEL) {
         return -EINVAL;
     }
     if (logId == LOG_ID_SECURITY) {
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index ed11164..80bb673 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -11,7 +11,7 @@
     onrestart restart media
     onrestart restart netd
     onrestart restart wificond
-    writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+    writepid /dev/cpuset/foreground/tasks
 
 service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
     class main
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 66e7750..36bb443 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -11,7 +11,7 @@
     onrestart restart media
     onrestart restart netd
     onrestart restart wificond
-    writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+    writepid /dev/cpuset/foreground/tasks
 
 service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
     class main