Merge "Set up /data/fonts directory for updatable system fonts"
diff --git a/code_coverage/seccomp_policy/code_coverage.arm.policy b/code_coverage/seccomp_policy/code_coverage.arm.policy
index b80910f..3589379 100644
--- a/code_coverage/seccomp_policy/code_coverage.arm.policy
+++ b/code_coverage/seccomp_policy/code_coverage.arm.policy
@@ -1,4 +1,5 @@
 close: 1
+fchmod: 1
 mkdirat: 1
 msync: 1
 munmap: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.arm64.policy b/code_coverage/seccomp_policy/code_coverage.arm64.policy
index 7040ea2..fdb4d1e 100644
--- a/code_coverage/seccomp_policy/code_coverage.arm64.policy
+++ b/code_coverage/seccomp_policy/code_coverage.arm64.policy
@@ -1,4 +1,5 @@
 close: 1
+fchmod: 1
 mkdirat: 1
 msync: 1
 munmap: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.policy.def b/code_coverage/seccomp_policy/code_coverage.policy.def
index 599c4a4..b6a4c6d 100644
--- a/code_coverage/seccomp_policy/code_coverage.policy.def
+++ b/code_coverage/seccomp_policy/code_coverage.policy.def
@@ -13,6 +13,9 @@
 // 2nd-Nth: uses mmap() to update in place
 
 close: 1
+// fchmod allowed to set libprofile-clang-extras, which wraps `open` calls, to
+// set correct permission for coverage files.
+fchmod: 1
 mkdirat: 1
 msync: 1
 munmap: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.x86.policy b/code_coverage/seccomp_policy/code_coverage.x86.policy
index f8e0cc0..145d3a3 100644
--- a/code_coverage/seccomp_policy/code_coverage.x86.policy
+++ b/code_coverage/seccomp_policy/code_coverage.x86.policy
@@ -1,4 +1,5 @@
 close: 1
+fchmod: 1
 mkdirat: 1
 msync: 1
 munmap: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.x86_64.policy b/code_coverage/seccomp_policy/code_coverage.x86_64.policy
index dcf2f9a..11c8075 100644
--- a/code_coverage/seccomp_policy/code_coverage.x86_64.policy
+++ b/code_coverage/seccomp_policy/code_coverage.x86_64.policy
@@ -1,4 +1,5 @@
 close: 1
+fchmod: 1
 mkdirat: 1
 msync: 1
 munmap: 1
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 6391acc..fd62392 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -225,9 +225,6 @@
         debuggable: {
             cflags: ["-DROOT_POSSIBLE"],
         },
-        experimental_mte: {
-            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
-        },
     },
 }
 
@@ -297,12 +294,6 @@
     },
 
     test_suites: ["device-tests"],
-
-    product_variables: {
-        experimental_mte: {
-            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
-        },
-    },
 }
 
 cc_benchmark {
@@ -353,12 +344,6 @@
     apex_available: [
         "com.android.runtime",
     ],
-
-    product_variables: {
-        experimental_mte: {
-            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
-        },
-    },
 }
 
 cc_binary {
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index b3e81b0..4f60005 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -40,7 +40,6 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#include <bionic/mte_kernel.h>
 #include <bionic/reserved_signals.h>
 #include <cutils/sockets.h>
 #include <log/log.h>
@@ -484,7 +483,6 @@
         continue;
       }
 
-#ifdef ANDROID_EXPERIMENTAL_MTE
       struct iovec iov = {
           &info.tagged_addr_ctrl,
           sizeof(info.tagged_addr_ctrl),
@@ -493,7 +491,6 @@
                  reinterpret_cast<void*>(&iov)) == -1) {
         info.tagged_addr_ctrl = -1;
       }
-#endif
 
       if (thread == g_target_thread) {
         // Read the thread's registers along with the rest of the crash info out of the pipe.
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 5565e8b..45e555f 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -16,6 +16,7 @@
 
 #include <err.h>
 #include <fcntl.h>
+#include <malloc.h>
 #include <stdlib.h>
 #include <sys/capability.h>
 #include <sys/mman.h>
@@ -32,9 +33,7 @@
 
 #include <android/fdsan.h>
 #include <android/set_abort_message.h>
-#include <bionic/malloc.h>
 #include <bionic/mte.h>
-#include <bionic/mte_kernel.h>
 #include <bionic/reserved_signals.h>
 
 #include <android-base/cmsg.h>
@@ -384,27 +383,16 @@
 #endif
 }
 
-#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+#if defined(__aarch64__)
 static void SetTagCheckingLevelSync() {
-  int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
-  if (tagged_addr_ctrl < 0) {
-    abort();
-  }
-
-  tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_SYNC;
-  if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) != 0) {
-    abort();
-  }
-
-  HeapTaggingLevel heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC;
-  if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level))) {
+  if (mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, M_HEAP_TAGGING_LEVEL_SYNC) == 0) {
     abort();
   }
 }
 #endif
 
 TEST_F(CrasherTest, mte_uaf) {
-#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+#if defined(__aarch64__)
   if (!mte_supported()) {
     GTEST_SKIP() << "Requires MTE";
   }
@@ -436,12 +424,12 @@
   ASSERT_MATCH(result, R"(deallocated by thread .*
       #00 pc)");
 #else
-  GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+  GTEST_SKIP() << "Requires aarch64";
 #endif
 }
 
 TEST_F(CrasherTest, mte_overflow) {
-#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+#if defined(__aarch64__)
   if (!mte_supported()) {
     GTEST_SKIP() << "Requires MTE";
   }
@@ -470,12 +458,12 @@
 allocated by thread .*
       #00 pc)");
 #else
-  GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+  GTEST_SKIP() << "Requires aarch64";
 #endif
 }
 
 TEST_F(CrasherTest, mte_underflow) {
-#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+#if defined(__aarch64__)
   if (!mte_supported()) {
     GTEST_SKIP() << "Requires MTE";
   }
@@ -504,12 +492,12 @@
 allocated by thread .*
       #00 pc)");
 #else
-  GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+  GTEST_SKIP() << "Requires aarch64";
 #endif
 }
 
 TEST_F(CrasherTest, mte_multiple_causes) {
-#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+#if defined(__aarch64__)
   if (!mte_supported()) {
     GTEST_SKIP() << "Requires MTE";
   }
@@ -558,11 +546,11 @@
   // overflows), so we can't match explicitly for an underflow message.
   ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation)");
 #else
-  GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+  GTEST_SKIP() << "Requires aarch64";
 #endif
 }
 
-#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+#if defined(__aarch64__)
 static uintptr_t CreateTagMapping() {
   uintptr_t mapping =
       reinterpret_cast<uintptr_t>(mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE | PROT_MTE,
@@ -579,7 +567,7 @@
 #endif
 
 TEST_F(CrasherTest, mte_tag_dump) {
-#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+#if defined(__aarch64__)
   if (!mte_supported()) {
     GTEST_SKIP() << "Requires MTE";
   }
@@ -607,7 +595,7 @@
     01.............0 0000000000000000 0000000000000000  ................
     00.............0)");
 #else
-  GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+  GTEST_SKIP() << "Requires aarch64";
 #endif
 }
 
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 85ffc98..1297c4d 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -53,7 +53,6 @@
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
 #include <bionic/reserved_signals.h>
-#include <cutils/properties.h>
 
 #include <libdebuggerd/utility.h>
 
diff --git a/debuggerd/libdebuggerd/test/UnwinderMock.h b/debuggerd/libdebuggerd/test/UnwinderMock.h
index 023a578..44a9214 100644
--- a/debuggerd/libdebuggerd/test/UnwinderMock.h
+++ b/debuggerd/libdebuggerd/test/UnwinderMock.h
@@ -34,7 +34,7 @@
     unwindstack::MapInfo* map_info = GetMaps()->Find(offset);
     if (map_info != nullptr) {
       std::string* new_build_id = new std::string(build_id);
-      map_info->build_id = reinterpret_cast<uintptr_t>(new_build_id);
+      map_info->build_id = new_build_id;
     }
   }
 };
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index d7067ca..7826efc 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -35,7 +35,6 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#include <bionic/mte_kernel.h>
 #include <bionic/reserved_signals.h>
 #include <debuggerd/handler.h>
 #include <log/log.h>
@@ -370,14 +369,12 @@
           return "SEGV_ADIDERR";
         case SEGV_ADIPERR:
           return "SEGV_ADIPERR";
-#if defined(ANDROID_EXPERIMENTAL_MTE)
         case SEGV_MTEAERR:
           return "SEGV_MTEAERR";
         case SEGV_MTESERR:
           return "SEGV_MTESERR";
-#endif
       }
-      static_assert(NSIGSEGV == SEGV_ADIPERR, "missing SEGV_* si_code");
+      static_assert(NSIGSEGV == SEGV_MTESERR, "missing SEGV_* si_code");
       break;
     case SIGSYS:
       switch (si->si_code) {
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index 7d25c50..6cbf12c 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -27,7 +27,6 @@
 #include <android-base/cmsg.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
-#include <cutils/sockets.h>
 
 #include "protocol.h"
 #include "util.h"
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 9d09210..f3bff8c 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -16,7 +16,6 @@
 
 #include "util.h"
 
-#include <sys/socket.h>
 #include <time.h>
 
 #include <string>
@@ -25,7 +24,6 @@
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <cutils/sockets.h>
 #include "protocol.h"
 
 std::string get_process_name(pid_t pid) {
diff --git a/deprecated-adf/OWNERS b/deprecated-adf/OWNERS
deleted file mode 100644
index 72b8b5a..0000000
--- a/deprecated-adf/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-ghackmann@google.com
-marissaw@google.com
diff --git a/deprecated-adf/libadf/Android.bp b/deprecated-adf/libadf/Android.bp
deleted file mode 100644
index 70f0a3b..0000000
--- a/deprecated-adf/libadf/Android.bp
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-cc_library {
-    name: "libadf",
-    recovery_available: true,
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
-    srcs: ["adf.cpp"],
-    cflags: ["-Werror"],
-    local_include_dirs: ["include"],
-    export_include_dirs: ["include"],
-}
diff --git a/deprecated-adf/libadf/adf.cpp b/deprecated-adf/libadf/adf.cpp
deleted file mode 100644
index fd9c208..0000000
--- a/deprecated-adf/libadf/adf.cpp
+++ /dev/null
@@ -1,746 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <malloc.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <memory>
-#include <vector>
-
-#include <linux/limits.h>
-
-#include <sys/ioctl.h>
-
-#include <adf/adf.h>
-
-#define ADF_BASE_PATH "/dev/"
-
-static ssize_t adf_id_vector_to_array(const std::vector<adf_id_t> &in,
-        adf_id_t **out)
-{
-    auto size = sizeof(in[0]) * in.size();
-    // We can't use new[] since the existing API says the caller should free()
-    // the returned array
-    auto ret = static_cast<adf_id_t *>(malloc(size));
-    if (!ret)
-        return -ENOMEM;
-
-    std::copy(in.begin(), in.end(), ret);
-    *out = ret;
-    return in.size();
-}
-
-static ssize_t adf_find_nodes(const char *pattern, adf_id_t **ids_out)
-{
-    struct dirent *dirent;
-    std::unique_ptr<DIR, decltype(&closedir)>
-        dir{opendir(ADF_BASE_PATH), closedir};
-    if (!dir)
-        return -errno;
-
-    std::vector<adf_id_t> ids;
-    errno = 0;
-    while ((dirent = readdir(dir.get()))) {
-        adf_id_t id;
-        int matched = sscanf(dirent->d_name, pattern, &id);
-
-        if (matched < 0)
-            return -errno;
-        else if (matched == 1)
-            ids.push_back(id);
-    }
-    if (errno)
-        return -errno;
-
-    return adf_id_vector_to_array(ids, ids_out);
-}
-
-ssize_t adf_devices(adf_id_t **ids)
-{
-    return adf_find_nodes("adf%u", ids);
-}
-
-int adf_device_open(adf_id_t id, int flags, struct adf_device *dev)
-{
-    char filename[64];
-
-    dev->id = id;
-
-    snprintf(filename, sizeof(filename), ADF_BASE_PATH "adf%u", id);
-    dev->fd = open(filename, flags);
-    if (dev->fd < 0)
-        return -errno;
-
-    return 0;
-}
-
-void adf_device_close(struct adf_device *dev)
-{
-    if (dev->fd >= 0)
-        close(dev->fd);
-}
-
-int adf_get_device_data(struct adf_device *dev, struct adf_device_data *data)
-{
-    int err;
-    int ret = 0;
-
-    memset(data, 0, sizeof(*data));
-
-    err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data);
-    if (err < 0)
-        return -ENOMEM;
-
-    if (data->n_attachments)
-        data->attachments = new adf_attachment_config[data->n_attachments];
-
-    if (data->n_allowed_attachments)
-        data->allowed_attachments =
-                new adf_attachment_config[data->n_allowed_attachments];
-
-    if (data->custom_data_size)
-        data->custom_data = new char[data->custom_data_size];
-
-    err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data);
-    if (err < 0) {
-        ret = -errno;
-        adf_free_device_data(data);
-    }
-    return ret;
-}
-
-void adf_free_device_data(struct adf_device_data *data)
-{
-    delete [] data->attachments;
-    data->attachments = nullptr;
-    delete [] data->allowed_attachments;
-    data->allowed_attachments = nullptr;
-    delete [] static_cast<char *>(data->custom_data);
-    data->custom_data = nullptr;
-}
-
-int adf_device_post(struct adf_device *dev,
-        adf_id_t *interfaces, size_t n_interfaces,
-        struct adf_buffer_config *bufs, size_t n_bufs,
-        void *custom_data, size_t custom_data_size)
-{
-    int err;
-    struct adf_post_config data;
-
-    memset(&data, 0, sizeof(data));
-    data.interfaces = interfaces;
-    data.n_interfaces = n_interfaces;
-    data.bufs = bufs;
-    data.n_bufs = n_bufs;
-    data.custom_data = custom_data;
-    data.custom_data_size = custom_data_size;
-
-    err = ioctl(dev->fd, ADF_POST_CONFIG, &data);
-    if (err < 0)
-        return -errno;
-
-    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)
-{
-    int err;
-    struct adf_attachment_config data;
-
-    memset(&data, 0, sizeof(data));
-    data.overlay_engine = overlay_engine;
-    data.interface = interface;
-
-    err = ioctl(dev->fd, attach ? ADF_ATTACH : ADF_DETACH, &data);
-    if (err < 0)
-        return -errno;
-
-    return 0;
-}
-
-int adf_device_attach(struct adf_device *dev, adf_id_t overlay_engine,
-                      adf_id_t interface)
-{
-   return adf_device_attachment(dev, overlay_engine, interface, true);
-}
-
-int adf_device_detach(struct adf_device *dev, adf_id_t overlay_engine,
-                      adf_id_t interface)
-{
-   return adf_device_attachment(dev, overlay_engine, interface, false);
-}
-
-ssize_t adf_interfaces(struct adf_device *dev, adf_id_t **interfaces)
-{
-    char pattern[64];
-
-    snprintf(pattern, sizeof(pattern), "adf-interface%u.%%u", dev->id);
-    return adf_find_nodes(pattern, interfaces);
-}
-
-ssize_t adf_interfaces_for_overlay_engine(struct adf_device *dev,
-        adf_id_t overlay_engine, adf_id_t **interfaces)
-{
-    struct adf_device_data data;
-    auto err = adf_get_device_data(dev, &data);
-    if (err < 0)
-        return err;
-
-    std::vector<adf_id_t> ids;
-    if (data.allowed_attachments != nullptr)
-        for (size_t i = 0; i < data.n_allowed_attachments; i++)
-            if (data.allowed_attachments[i].overlay_engine == overlay_engine)
-              ids.push_back(data.allowed_attachments[i].interface);
-
-    adf_free_device_data(&data);
-    return adf_id_vector_to_array(ids, interfaces);
-}
-
-static ssize_t adf_interfaces_filter(struct adf_device *dev,
-        adf_id_t *in, size_t n_in, adf_id_t **out,
-        bool (*filter)(struct adf_interface_data *data, __u32 match),
-        __u32 match)
-{
-    std::vector<adf_id_t> ids;
-    for (size_t i = 0; i < n_in; i++) {
-        int fd = adf_interface_open(dev, in[i], O_RDONLY);
-        if (fd < 0)
-            return fd;
-
-        struct adf_interface_data data;
-        auto ret = adf_get_interface_data(fd, &data);
-        close(fd);
-        if (ret < 0)
-            return ret;
-
-        if (filter(&data, match))
-            ids.push_back(in[i]);
-    }
-
-    return adf_id_vector_to_array(ids, out);
-}
-
-static bool adf_interface_type_filter(struct adf_interface_data *data,
-        __u32 type)
-{
-    return data->type == (enum adf_interface_type)type;
-}
-
-ssize_t adf_interfaces_filter_by_type(struct adf_device *dev,
-        enum adf_interface_type type,
-        adf_id_t *in, size_t n_in, adf_id_t **out)
-{
-    return adf_interfaces_filter(dev, in, n_in, out, adf_interface_type_filter,
-            type);
-}
-
-static bool adf_interface_flags_filter(struct adf_interface_data *data,
-        __u32 flag)
-{
-    return !!(data->flags & flag);
-}
-
-ssize_t adf_interfaces_filter_by_flag(struct adf_device *dev, __u32 flag,
-        adf_id_t *in, size_t n_in, adf_id_t **out)
-{
-    return adf_interfaces_filter(dev, in, n_in, out, adf_interface_flags_filter,
-            flag);
-}
-
-int adf_interface_open(struct adf_device *dev, adf_id_t id, int flags)
-{
-    char filename[64];
-
-    snprintf(filename, sizeof(filename), ADF_BASE_PATH "adf-interface%u.%u",
-            dev->id, id);
-
-    int fd = open(filename, flags);
-    if (fd < 0)
-        return -errno;
-    return fd;
-}
-
-int adf_get_interface_data(int fd, struct adf_interface_data *data)
-{
-    int err;
-    int ret = 0;
-
-    memset(data, 0, sizeof(*data));
-
-    err = ioctl(fd, ADF_GET_INTERFACE_DATA, data);
-    if (err < 0)
-        return -errno;
-
-    if (data->n_available_modes)
-        data->available_modes = new drm_mode_modeinfo[data->n_available_modes];
-
-    if (data->custom_data_size)
-        data->custom_data = new char[data->custom_data_size];
-
-    err = ioctl(fd, ADF_GET_INTERFACE_DATA, data);
-    if (err < 0) {
-        ret = -errno;
-        adf_free_interface_data(data);
-    }
-    return ret;
-}
-
-void adf_free_interface_data(struct adf_interface_data *data)
-{
-    delete [] data->available_modes;
-    delete [] static_cast<char *>(data->custom_data);
-}
-
-int adf_interface_blank(int fd, __u8 mode)
-{
-    int err = ioctl(fd, ADF_BLANK, mode);
-    if (err < 0)
-        return -errno;
-    return 0;
-}
-
-int adf_interface_set_mode(int fd, struct drm_mode_modeinfo *mode)
-{
-    int err = ioctl(fd, ADF_SET_MODE, mode);
-    if (err < 0)
-        return -errno;
-    return 0;
-}
-
-int adf_interface_simple_buffer_alloc(int fd, __u32 w, __u32 h,
-        __u32 format, __u32 *offset, __u32 *pitch)
-{
-    int err;
-    struct adf_simple_buffer_alloc data;
-
-    memset(&data, 0, sizeof(data));
-    data.w = w;
-    data.h = h;
-    data.format = format;
-
-    err = ioctl(fd, ADF_SIMPLE_BUFFER_ALLOC, &data);
-    if (err < 0)
-        return -errno;
-
-    *offset = data.offset;
-    *pitch = data.pitch;
-    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)
-{
-    int ret;
-    struct adf_simple_post_config 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);
-    ret = ioctl(fd, ADF_SIMPLE_POST_CONFIG, &data);
-    if (ret < 0)
-        return -errno;
-
-    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];
-
-    snprintf(pattern, sizeof(pattern), "adf-overlay-engine%u.%%u", dev->id);
-    return adf_find_nodes(pattern, overlay_engines);
-}
-
-ssize_t adf_overlay_engines_for_interface(struct adf_device *dev,
-        adf_id_t interface, adf_id_t **overlay_engines)
-{
-    struct adf_device_data data;
-    auto err = adf_get_device_data(dev, &data);
-    if (err < 0)
-        return err;
-
-    std::vector<adf_id_t> ids;
-    if (data.allowed_attachments != nullptr)
-        for (size_t i = 0; i < data.n_allowed_attachments; i++)
-            if (data.allowed_attachments[i].interface == interface)
-                ids.push_back(data.allowed_attachments[i].overlay_engine);
-
-    return adf_id_vector_to_array(ids, overlay_engines);
-}
-
-static ssize_t adf_overlay_engines_filter(struct adf_device *dev,
-        adf_id_t *in, size_t n_in, adf_id_t **out,
-        bool (*filter)(struct adf_overlay_engine_data *data, void *cookie),
-        void *cookie)
-{
-    std::vector<adf_id_t> ids;
-    size_t i;
-    for (i = 0; i < n_in; i++) {
-        int fd = adf_overlay_engine_open(dev, in[i], O_RDONLY);
-        if (fd < 0)
-            return fd;
-
-        struct adf_overlay_engine_data data;
-        auto ret = adf_get_overlay_engine_data(fd, &data);
-        close(fd);
-        if (ret < 0)
-            return ret;
-
-        if (filter(&data, cookie))
-            ids.push_back(in[i]);
-    }
-
-    return adf_id_vector_to_array(ids, out);
-}
-
-struct format_filter_cookie {
-    const __u32 *formats;
-    size_t n_formats;
-};
-
-static bool adf_overlay_engine_format_filter(
-        struct adf_overlay_engine_data *data, void *cookie)
-{
-    auto c = static_cast<format_filter_cookie *>(cookie);
-    size_t i;
-    for (i = 0; i < data->n_supported_formats; i++) {
-        size_t j;
-        for (j = 0; j < c->n_formats; j++)
-            if (data->supported_formats[i] == c->formats[j])
-                return true;
-    }
-    return false;
-}
-
-ssize_t adf_overlay_engines_filter_by_format(struct adf_device *dev,
-        const __u32 *formats, size_t n_formats, adf_id_t *in, size_t n_in,
-        adf_id_t **out)
-{
-    struct format_filter_cookie cookie = { formats, n_formats };
-    return adf_overlay_engines_filter(dev, in, n_in, out,
-            adf_overlay_engine_format_filter, &cookie);
-}
-
-int adf_overlay_engine_open(struct adf_device *dev, adf_id_t id, int flags)
-{
-    char filename[64];
-
-    snprintf(filename, sizeof(filename),
-            ADF_BASE_PATH "adf-overlay-engine%u.%u", dev->id, id);
-
-    int fd = open(filename, flags);
-    if (fd < 0)
-        return -errno;
-    return fd;
-}
-
-int adf_get_overlay_engine_data(int fd, struct adf_overlay_engine_data *data)
-{
-    int err;
-    int ret = 0;
-
-    memset(data, 0, sizeof(*data));
-
-    err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data);
-    if (err < 0)
-        return -errno;
-
-    if (data->n_supported_formats)
-        data->supported_formats = new __u32[data->n_supported_formats];
-
-    if (data->custom_data_size)
-      data->custom_data = new char[data->custom_data_size];
-
-    err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data);
-    if (err < 0) {
-        ret = -errno;
-        adf_free_overlay_engine_data(data);
-    }
-    return ret;
-}
-
-void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data)
-{
-    delete [] data->supported_formats;
-    data->supported_formats = nullptr;
-    delete [] static_cast<char *>(data->custom_data);
-    data->custom_data = nullptr;
-}
-
-bool adf_overlay_engine_supports_format(int fd, __u32 format)
-{
-    struct adf_overlay_engine_data data;
-    bool ret = false;
-    size_t i;
-
-    int err = adf_get_overlay_engine_data(fd, &data);
-    if (err < 0)
-        return false;
-
-    if (data.supported_formats != nullptr) {
-        for (i = 0; i < data.n_supported_formats; i++) {
-            if (data.supported_formats[i] == format) {
-                ret = true;
-                break;
-            }
-        }
-    }
-
-    adf_free_overlay_engine_data(&data);
-    return ret;
-}
-
-int adf_set_event(int fd, enum adf_event_type type, bool enabled)
-{
-    struct adf_set_event data;
-
-    data.type = type;
-    data.enabled = enabled;
-
-    int err = ioctl(fd, ADF_SET_EVENT, &data);
-    if (err < 0)
-        return -errno;
-    return 0;
-}
-
-int adf_read_event(int fd, struct adf_event **event)
-{
-    struct adf_event header;
-    struct event_with_data {
-        struct adf_event base;
-        uint8_t data[0];
-    };
-    using unique_event = std::unique_ptr<event_with_data, decltype(&free)>;
-    size_t data_size;
-
-    int err = read(fd, &header, sizeof(header));
-    if (err < 0)
-        return -errno;
-    if ((size_t)err < sizeof(header))
-        return -EIO;
-    if (header.length < sizeof(header))
-        return -EIO;
-
-    // Again, we can't use new[] since the existing API says the caller should
-    // free() the returned event
-    auto event_ptr = static_cast<event_with_data *>(malloc(header.length));
-    unique_event event_ret{event_ptr, free};
-    if (!event_ret)
-        return -ENOMEM;
-    data_size = header.length - sizeof(header);
-
-    memcpy(event_ret.get(), &header, sizeof(header));
-    ssize_t read_size = read(fd, &event_ret->data, data_size);
-    if (read_size < 0)
-        return -errno;
-    if ((size_t)read_size < data_size)
-        return -EIO;
-
-    *event = &event_ret.release()->base;
-    return 0;
-}
-
-void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE])
-{
-    buf[0] = format & 0xFF;
-    buf[1] = (format >> 8) & 0xFF;
-    buf[2] = (format >> 16) & 0xFF;
-    buf[3] = (format >> 24) & 0xFF;
-    buf[4] = '\0';
-}
-
-static bool adf_find_simple_post_overlay_engine(struct adf_device *dev,
-        const __u32 *formats, size_t n_formats,
-        adf_id_t interface, adf_id_t *overlay_engine)
-{
-    adf_id_t *engs = nullptr;
-    ssize_t n_engs = adf_overlay_engines_for_interface(dev, interface, &engs);
-
-    if (engs == nullptr)
-        return false;
-
-    adf_id_t *filtered_engs = nullptr;
-    ssize_t n_filtered_engs = adf_overlay_engines_filter_by_format(dev,
-            formats, n_formats, engs, n_engs, &filtered_engs);
-    free(engs);
-
-    if (filtered_engs == nullptr)
-        return false;
-
-    *overlay_engine = filtered_engs[0];
-    free(filtered_engs);
-    return true;
-}
-
-static const __u32 any_rgb_format[] = {
-    DRM_FORMAT_C8,
-    DRM_FORMAT_RGB332,
-    DRM_FORMAT_BGR233,
-    DRM_FORMAT_XRGB1555,
-    DRM_FORMAT_XBGR1555,
-    DRM_FORMAT_RGBX5551,
-    DRM_FORMAT_BGRX5551,
-    DRM_FORMAT_ARGB1555,
-    DRM_FORMAT_ABGR1555,
-    DRM_FORMAT_RGBA5551,
-    DRM_FORMAT_BGRA5551,
-    DRM_FORMAT_RGB565,
-    DRM_FORMAT_BGR565,
-    DRM_FORMAT_RGB888,
-    DRM_FORMAT_BGR888,
-    DRM_FORMAT_XRGB8888,
-    DRM_FORMAT_XBGR8888,
-    DRM_FORMAT_RGBX8888,
-    DRM_FORMAT_BGRX8888,
-    DRM_FORMAT_XRGB2101010,
-    DRM_FORMAT_XBGR2101010,
-    DRM_FORMAT_RGBX1010102,
-    DRM_FORMAT_BGRX1010102,
-    DRM_FORMAT_ARGB2101010,
-    DRM_FORMAT_ABGR2101010,
-    DRM_FORMAT_RGBA1010102,
-    DRM_FORMAT_BGRA1010102,
-    DRM_FORMAT_ARGB8888,
-    DRM_FORMAT_ABGR8888,
-    DRM_FORMAT_RGBA8888,
-    DRM_FORMAT_BGRA8888,
-};
-
-int adf_find_simple_post_configuration(struct adf_device *dev,
-        const __u32 *formats, size_t n_formats,
-        adf_id_t *interface, adf_id_t *overlay_engine)
-{
-    adf_id_t *intfs = NULL;
-    ssize_t n_intfs = adf_interfaces(dev, &intfs);
-
-    if (n_intfs < 0)
-        return n_intfs;
-    else if (!intfs)
-        return -ENODEV;
-
-    adf_id_t *primary_intfs = nullptr;
-    ssize_t n_primary_intfs = adf_interfaces_filter_by_flag(dev,
-            ADF_INTF_FLAG_PRIMARY, intfs, n_intfs, &primary_intfs);
-    free(intfs);
-
-    if (n_primary_intfs < 0)
-        return n_primary_intfs;
-    else if (!primary_intfs)
-        return -ENODEV;
-
-    if (!formats) {
-        formats = any_rgb_format;
-        n_formats = sizeof(any_rgb_format) / sizeof(any_rgb_format[0]);
-    }
-
-    bool found = false;
-    ssize_t i = 0;
-    for (i = 0; i < n_primary_intfs; i++) {
-        found = adf_find_simple_post_overlay_engine(dev, formats, n_formats,
-                primary_intfs[i], overlay_engine);
-        if (found) {
-            *interface = primary_intfs[i];
-            break;
-        }
-    }
-    free(primary_intfs);
-
-    if (!found)
-        return -ENODEV;
-
-    return 0;
-}
diff --git a/deprecated-adf/libadf/include/adf/adf.h b/deprecated-adf/libadf/include/adf/adf.h
deleted file mode 100644
index e4c7b28..0000000
--- a/deprecated-adf/libadf/include/adf/adf.h
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright (C) 2013 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 _LIBADF_ADF_H_
-#define _LIBADF_ADF_H_
-
-#include <stdint.h>
-#include <stdbool.h>
-#include <sys/cdefs.h>
-#include <sys/types.h>
-#include <video/adf.h>
-
-typedef __u32 adf_id_t;
-
-struct adf_device {
-    adf_id_t id;
-    int fd;
-};
-
-__BEGIN_DECLS
-
-/**
- * Enumerates all ADF devices.
- *
- * Returns the number of ADF devices, and sets ids to a list of device IDs.
- * The caller must free() the returned list of device IDs.
- *
- * On error, returns -errno.
- */
-ssize_t adf_devices(adf_id_t **ids);
-
-/**
- * Opens an ADF device.
- *
- * On error, returns -errno.
- */
-int adf_device_open(adf_id_t id, int flags, struct adf_device *dev);
-/**
- * Closes an ADF device.
- */
-void adf_device_close(struct adf_device *dev);
-/**
- * Reads the ADF device data.
- *
- * adf_get_device_data() allocates buffers inside data, which the caller
- * must free by calling adf_free_device_data().  On error, returns -errno.
- */
-int adf_get_device_data(struct adf_device *dev, struct adf_device_data *data);
-/**
- * Frees the device data returned by adf_get_device_data().
- */
-void adf_free_device_data(struct adf_device_data *data);
-
-/**
- * Atomically posts a new display configuration to the specified interfaces.
- *
- * Returns a sync fence fd that will fire when the configuration is removed
- * from the screen.  On error, returns -errno.
- */
-int adf_device_post(struct adf_device *dev,
-        adf_id_t *interfaces, size_t n_interfaces,
-        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,
-                      adf_id_t interface);
-/**
- * Detaches the specified interface and overlay engine.
- */
-int adf_device_detach(struct adf_device *dev, adf_id_t overlay_engine,
-                      adf_id_t interface);
-
-/**
- * Enumerates all interfaces belonging to an ADF device.
- *
- * The caller must free() the returned list of interface IDs.
- */
-ssize_t adf_interfaces(struct adf_device *dev, adf_id_t **interfaces);
-
-/**
- * Enumerates all interfaces which can be attached to the specified overlay
- * engine.
- *
- * The caller must free() the returned list of interface IDs.
- */
-ssize_t adf_interfaces_for_overlay_engine(struct adf_device *dev,
-        adf_id_t overlay_engine, adf_id_t **interfaces);
-/**
- * Filters a list of interfaces by type.
- *
- * Returns the number of matching interfaces, and sets out to a list of matching
- * interface IDs.  The caller must free() the returned list of interface IDs.
- *
- * On error, returns -errno.
- */
-ssize_t adf_interfaces_filter_by_type(struct adf_device *dev,
-        enum adf_interface_type type,
-        adf_id_t *in, size_t n_in, adf_id_t **out);
-/**
- * Filters a list of interfaces by flag.
- *
- * The caller must free() the returned list of interface IDs.
- */
-ssize_t adf_interfaces_filter_by_flag(struct adf_device *dev, __u32 flag,
-        adf_id_t *in, size_t n_in, adf_id_t **out);
-
-/**
- * Opens an ADF interface.
- *
- * Returns a file descriptor.  The caller must close() the fd when done.
- * On error, returns -errno.
- */
-int adf_interface_open(struct adf_device *dev, adf_id_t id, int flags);
-/**
- * Reads the interface data.
- *
- * adf_get_interface_data() allocates buffers inside data, which the caller
- * must free by calling adf_free_interface_data().  On error, returns -errno.
- */
-int adf_get_interface_data(int fd, struct adf_interface_data *data);
-/**
- * Frees the interface data returned by adf_get_interface_data().
- */
-void adf_free_interface_data(struct adf_interface_data *data);
-
-/**
- * Sets the interface's DPMS mode.
- */
-int adf_interface_blank(int fd, __u8 mode);
-/**
- * Sets the interface's display mode.
- */
-int adf_interface_set_mode(int fd, struct drm_mode_modeinfo *mode);
-/**
- * Allocates a single-plane RGB buffer of the specified size and format.
- *
- * Returns a dma-buf fd.  On error, returns -errno.
- */
-int adf_interface_simple_buffer_alloc(int fd, __u32 w, __u32 h,
-        __u32 format, __u32 *offset, __u32 *pitch);
-/**
- * Posts a single-plane RGB buffer to the display using the specified
- * overlay engine.
- *
- * Returns a sync fence fd that will fire when the buffer is removed
- * from the screen.  On error, returns -errno.
- */
-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.
- *
- * The caller must free() the returned list of overlay engine IDs.
- */
-ssize_t adf_overlay_engines(struct adf_device *dev, adf_id_t **overlay_engines);
-
-/**
- * Enumerates all overlay engines which can be attached to the specified
- * interface.
- *
- * The caller must free() the returned list of overlay engine IDs.
- */
-ssize_t adf_overlay_engines_for_interface(struct adf_device *dev,
-        adf_id_t interface, adf_id_t **overlay_engines);
-/**
- * Filters a list of overlay engines by supported buffer format.
- *
- * Returns the overlay engines which support at least one of the specified
- * formats.  The caller must free() the returned list of overlay engine IDs.
- */
-ssize_t adf_overlay_engines_filter_by_format(struct adf_device *dev,
-        const __u32 *formats, size_t n_formats, adf_id_t *in, size_t n_in,
-        adf_id_t **out);
-
-/**
- * Opens an ADF overlay engine.
- *
- * Returns a file descriptor.  The caller must close() the fd when done.
- * On error, returns -errno.
- */
-int adf_overlay_engine_open(struct adf_device *dev, adf_id_t id, int flags);
-/**
- * Reads the overlay engine data.
- *
- * adf_get_overlay_engine_data() allocates buffers inside data, which the caller
- * must free by calling adf_free_overlay_engine_data().  On error, returns
- * -errno.
- */
-int adf_get_overlay_engine_data(int fd, struct adf_overlay_engine_data *data);
-/**
- * Frees the overlay engine data returned by adf_get_overlay_engine_data().
- */
-void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data);
-
-/**
- * Returns whether the overlay engine supports the specified format.
- */
-bool adf_overlay_engine_supports_format(int fd, __u32 format);
-
-/**
- * Subscribes or unsubscribes from the specified hardware event.
- */
-int adf_set_event(int fd, enum adf_event_type type, bool enabled);
-/**
- * Reads one event from the fd, blocking if needed.
- *
- * The caller must free() the returned buffer.  On error, returns -errno.
- */
-int adf_read_event(int fd, struct adf_event **event);
-
-#define ADF_FORMAT_STR_SIZE 5
-/**
- * Converts an ADF/DRM fourcc format to its string representation.
- */
-void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE]);
-
-/**
- * Finds an appropriate interface and overlay engine for a simple post.
- *
- * Specifically, finds the primary interface, and an overlay engine
- * that can be attached to the primary interface and supports one of the
- * specified formats.  The caller may pass a NULL formats list, to indicate that
- * any RGB format is acceptable.
- *
- * On error, returns -errno.
- */
-int adf_find_simple_post_configuration(struct adf_device *dev,
-        const __u32 *formats, size_t n_formats,
-        adf_id_t *interface, adf_id_t *overlay_engine);
-
-__END_DECLS
-
-#endif /* _LIBADF_ADF_H_ */
diff --git a/deprecated-adf/libadf/include/video/adf.h b/deprecated-adf/libadf/include/video/adf.h
deleted file mode 100644
index 692a425..0000000
--- a/deprecated-adf/libadf/include/video/adf.h
+++ /dev/null
@@ -1,209 +0,0 @@
-/****************************************************************************
- ****************************************************************************
- ***
- ***   This header was automatically generated from a Linux kernel header
- ***   of the same name, to make information necessary for userspace to
- ***   call into the kernel available to libc.  It contains only constants,
- ***   structures, and macros generated from the original header, and thus,
- ***   contains no copyrightable information.
- ***
- ***   To edit the content of this header, modify the corresponding
- ***   source file (e.g. under external/kernel-headers/original/) then
- ***   run bionic/libc/kernel/tools/update_all.py
- ***
- ***   Any manual change here will be lost the next time this script will
- ***   be run. You've been warned!
- ***
- ****************************************************************************
- ****************************************************************************/
-#ifndef _UAPI_VIDEO_ADF_H_
-#define _UAPI_VIDEO_ADF_H_
-#include <linux/ioctl.h>
-#include <linux/types.h>
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#include <drm/drm_fourcc.h>
-#include <drm/drm_mode.h>
-#define ADF_NAME_LEN 32
-#define ADF_MAX_CUSTOM_DATA_SIZE 4096
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-enum adf_interface_type {
-  ADF_INTF_DSI = 0,
-  ADF_INTF_eDP = 1,
-  ADF_INTF_DPI = 2,
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  ADF_INTF_VGA = 3,
-  ADF_INTF_DVI = 4,
-  ADF_INTF_HDMI = 5,
-  ADF_INTF_MEMORY = 6,
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  ADF_INTF_TYPE_DEVICE_CUSTOM = 128,
-  ADF_INTF_TYPE_MAX = (~(__u32) 0),
-};
-#define ADF_INTF_FLAG_PRIMARY (1 << 0)
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define ADF_INTF_FLAG_EXTERNAL (1 << 1)
-enum adf_event_type {
-  ADF_EVENT_VSYNC = 0,
-  ADF_EVENT_HOTPLUG = 1,
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  ADF_EVENT_DEVICE_CUSTOM = 128,
-  ADF_EVENT_TYPE_MAX = 255,
-};
-enum adf_complete_fence_type {
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  ADF_COMPLETE_FENCE_NONE = 0,
-  ADF_COMPLETE_FENCE_PRESENT = 1,
-  ADF_COMPLETE_FENCE_RELEASE = 2,
-};
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-struct adf_set_event {
-  __u8 type;
-  __u8 enabled;
-};
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-struct adf_event {
-  __u8 type;
-  __u32 length;
-};
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-struct adf_vsync_event {
-  struct adf_event base;
-  __aligned_u64 timestamp;
-};
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-struct adf_hotplug_event {
-  struct adf_event base;
-  __u8 connected;
-};
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define ADF_MAX_PLANES 4
-struct adf_buffer_config {
-  __u32 overlay_engine;
-  __u32 w;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  __u32 h;
-  __u32 format;
-  __s32 fd[ADF_MAX_PLANES];
-  __u32 offset[ADF_MAX_PLANES];
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  __u32 pitch[ADF_MAX_PLANES];
-  __u8 n_planes;
-  __s32 acquire_fence;
-};
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define ADF_MAX_BUFFERS (4096 / sizeof(struct adf_buffer_config))
-struct adf_post_config {
-  size_t n_interfaces;
-  __u32 __user * interfaces;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  size_t n_bufs;
-  struct adf_buffer_config __user * bufs;
-  size_t custom_data_size;
-  void __user * custom_data;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  __s32 complete_fence;
-};
-struct adf_post_config_v2 {
-  __u32 n_interfaces;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  __u64 interfaces;
-  __u32 n_bufs;
-  __u64 bufs;
-  __u64 custom_data_size;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  __u64 custom_data;
-  __s32 complete_fence;
-  __u8 complete_fence_type;
-};
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define ADF_MAX_INTERFACES (4096 / sizeof(__u32))
-struct adf_simple_buffer_alloc {
-  __u16 w;
-  __u16 h;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  __u32 format;
-  __s32 fd;
-  __u32 offset;
-  __u32 pitch;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-};
-struct adf_simple_post_config {
-  struct adf_buffer_config buf;
-  __s32 complete_fence;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-};
-struct adf_simple_post_config_v2 {
-  struct adf_buffer_config buf;
-  __s32 complete_fence;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  __u8 complete_fence_type;
-};
-struct adf_attachment_config {
-  __u32 overlay_engine;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  __u32 interface;
-};
-struct adf_device_data {
-  char name[ADF_NAME_LEN];
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  size_t n_attachments;
-  struct adf_attachment_config __user * attachments;
-  size_t n_allowed_attachments;
-  struct adf_attachment_config __user * allowed_attachments;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  size_t custom_data_size;
-  void __user * custom_data;
-};
-#define ADF_MAX_ATTACHMENTS (4096 / sizeof(struct adf_attachment_config))
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-struct adf_interface_data {
-  char name[ADF_NAME_LEN];
-  __u32 type;
-  __u32 id;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  __u32 flags;
-  __u8 dpms_state;
-  __u8 hotplug_detect;
-  __u16 width_mm;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  __u16 height_mm;
-  struct drm_mode_modeinfo current_mode;
-  size_t n_available_modes;
-  struct drm_mode_modeinfo __user * available_modes;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  size_t custom_data_size;
-  void __user * custom_data;
-};
-#define ADF_MAX_MODES (4096 / sizeof(struct drm_mode_modeinfo))
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-struct adf_overlay_engine_data {
-  char name[ADF_NAME_LEN];
-  size_t n_supported_formats;
-  __u32 __user * supported_formats;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-  size_t custom_data_size;
-  void __user * custom_data;
-};
-#define ADF_MAX_SUPPORTED_FORMATS (4096 / sizeof(__u32))
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define ADF_IOCTL_TYPE 'D'
-#define ADF_IOCTL_NR_CUSTOM 128
-#define ADF_SET_EVENT _IOW(ADF_IOCTL_TYPE, 0, struct adf_set_event)
-#define ADF_BLANK _IOW(ADF_IOCTL_TYPE, 1, __u8)
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define ADF_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 2, struct adf_post_config)
-#define ADF_SET_MODE _IOW(ADF_IOCTL_TYPE, 3, struct drm_mode_modeinfo)
-#define ADF_GET_DEVICE_DATA _IOR(ADF_IOCTL_TYPE, 4, struct adf_device_data)
-#define ADF_GET_INTERFACE_DATA _IOR(ADF_IOCTL_TYPE, 5, struct adf_interface_data)
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define ADF_GET_OVERLAY_ENGINE_DATA _IOR(ADF_IOCTL_TYPE, 6, struct adf_overlay_engine_data)
-#define ADF_SIMPLE_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 7, struct adf_simple_post_config)
-#define ADF_SIMPLE_BUFFER_ALLOC _IOW(ADF_IOCTL_TYPE, 8, struct adf_simple_buffer_alloc)
-#define ADF_ATTACH _IOW(ADF_IOCTL_TYPE, 9, struct adf_attachment_config)
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define ADF_DETACH _IOW(ADF_IOCTL_TYPE, 10, struct adf_attachment_config)
-#define ADF_POST_CONFIG_V2 _IOW(ADF_IOCTL_TYPE, 11, struct adf_post_config_v2)
-#define ADF_SIMPLE_POST_CONFIG_V2 _IOW(ADF_IOCTL_TYPE, 12, struct adf_simple_post_config_v2)
-#endif
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-
diff --git a/deprecated-adf/libadf/original-kernel-headers/video/adf.h b/deprecated-adf/libadf/original-kernel-headers/video/adf.h
deleted file mode 100644
index 8293c1d..0000000
--- a/deprecated-adf/libadf/original-kernel-headers/video/adf.h
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * Copyright (C) 2013 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef _UAPI_VIDEO_ADF_H_
-#define _UAPI_VIDEO_ADF_H_
-
-#include <linux/ioctl.h>
-#include <linux/types.h>
-
-#include <drm/drm_fourcc.h>
-#include <drm/drm_mode.h>
-
-#define ADF_NAME_LEN 32
-#define ADF_MAX_CUSTOM_DATA_SIZE 4096
-
-enum adf_interface_type {
-	ADF_INTF_DSI = 0,
-	ADF_INTF_eDP = 1,
-	ADF_INTF_DPI = 2,
-	ADF_INTF_VGA = 3,
-	ADF_INTF_DVI = 4,
-	ADF_INTF_HDMI = 5,
-	ADF_INTF_MEMORY = 6,
-	ADF_INTF_TYPE_DEVICE_CUSTOM = 128,
-	ADF_INTF_TYPE_MAX = (~(__u32)0),
-};
-
-#define ADF_INTF_FLAG_PRIMARY (1 << 0)
-#define ADF_INTF_FLAG_EXTERNAL (1 << 1)
-
-enum adf_event_type {
-	ADF_EVENT_VSYNC = 0,
-	ADF_EVENT_HOTPLUG = 1,
-	ADF_EVENT_DEVICE_CUSTOM = 128,
-	ADF_EVENT_TYPE_MAX = 255,
-};
-
-enum adf_complete_fence_type {
-	/* no fence */
-	ADF_COMPLETE_FENCE_NONE = 0,
-	/* fence fires when the configuration appears on the screen */
-	ADF_COMPLETE_FENCE_PRESENT = 1,
-	/* fence fires when the configuration leaves the screen */
-	ADF_COMPLETE_FENCE_RELEASE = 2,
-};
-
-/**
- * struct adf_set_event - start or stop subscribing to ADF events
- *
- * @type: the type of event to (un)subscribe
- * @enabled: subscribe or unsubscribe
- *
- * After subscribing to an event, userspace may poll() the ADF object's fd
- * to wait for events or read() to consume the event's data.
- *
- * ADF reserves event types 0 to %ADF_EVENT_DEVICE_CUSTOM-1 for its own events.
- * Devices may use event types %ADF_EVENT_DEVICE_CUSTOM to %ADF_EVENT_TYPE_MAX-1
- * for driver-private events.
- */
-struct adf_set_event {
-	__u8 type;
-	__u8 enabled;
-};
-
-/**
- * struct adf_event - common header for ADF event data
- *
- * @type: event type
- * @length: total size of event data, header inclusive
- */
-struct adf_event {
-	__u8 type;
-	__u32 length;
-};
-
-/**
- * struct adf_vsync_event - ADF vsync event
- *
- * @base: event header (see &struct adf_event)
- * @timestamp: time of vsync event, in nanoseconds
- */
-struct adf_vsync_event {
-	struct adf_event base;
-	__aligned_u64 timestamp;
-};
-
-/**
- * struct adf_vsync_event - ADF display hotplug event
- *
- * @base: event header (see &struct adf_event)
- * @connected: whether a display is now connected to the interface
- */
-struct adf_hotplug_event {
-	struct adf_event base;
-	__u8 connected;
-};
-
-#define ADF_MAX_PLANES 4
-/**
- * struct adf_buffer_config - description of buffer displayed by adf_post_config
- *
- * @overlay_engine: id of the target overlay engine
- * @w: width of display region in pixels
- * @h: height of display region in pixels
- * @format: DRM-style fourcc, see drm_fourcc.h for standard formats
- * @fd: dma_buf fd for each plane
- * @offset: location of first pixel to scan out, in bytes
- * @pitch: stride (i.e. length of a scanline including padding) in bytes
- * @n_planes: number of planes in buffer
- * @acquire_fence: sync_fence fd which will clear when the buffer is
- *	ready for display, or <0 if the buffer is already ready
- */
-struct adf_buffer_config {
-	__u32 overlay_engine;
-
-	__u32 w;
-	__u32 h;
-	__u32 format;
-
-	__s32 fd[ADF_MAX_PLANES];
-	__u32 offset[ADF_MAX_PLANES];
-	__u32 pitch[ADF_MAX_PLANES];
-	__u8 n_planes;
-
-	__s32 acquire_fence;
-};
-#define ADF_MAX_BUFFERS (4096 / sizeof(struct adf_buffer_config))
-
-/**
- * struct adf_post_config - request to flip to a new set of buffers
- *
- * This request is equivalent to &struct adf_post_config_v2 with
- * @complete_fence_type = %ADF_COMPLETE_FENCE_RELEASE.
- *
- * @n_interfaces: number of interfaces targeted by the flip (input)
- * @interfaces: ids of interfaces targeted by the flip (input)
- * @n_bufs: number of buffers displayed (input)
- * @bufs: description of buffers displayed (input)
- * @custom_data_size: size of driver-private data (input)
- * @custom_data: driver-private data (input)
- * @complete_fence: sync_fence fd which will clear when this
- *	configuration has left the screen (output)
- */
-struct adf_post_config {
-	size_t n_interfaces;
-	__u32 __user *interfaces;
-
-	size_t n_bufs;
-	struct adf_buffer_config __user *bufs;
-
-	size_t custom_data_size;
-	void __user *custom_data;
-
-	__s32 complete_fence;
-};
-
-/**
- * struct adf_post_config_v2 - request to flip to a new set of buffers
- *
- * @n_interfaces: number of interfaces targeted by the flip (input)
- * @interfaces: ids of interfaces targeted by the flip (input)
- * @n_bufs: number of buffers displayed (input)
- * @bufs: description of buffers displayed (input)
- * @custom_data_size: size of driver-private data (input)
- * @custom_data: driver-private data (input)
- * @complete_fence_type: one of &enum adf_complete_fence_type describing what
- * 	fence to return (input)
- * @complete_fence: sync_fence fd which will fire at the time
- * 	requested by @complete_fence_type (output)
- */
-struct adf_post_config_v2 {
-	__u32 n_interfaces;
-	__u64 interfaces; /* __u32 * packed into __u64 */
-
-	__u32 n_bufs;
-	__u64 bufs; /* struct adf_buffer_config * packed into __u64 */
-
-	__u64 custom_data_size;
-	__u64 custom_data; /* void * packed into __u64 */
-
-	__s32 complete_fence;
-	__u8 complete_fence_type;
-};
-#define ADF_MAX_INTERFACES (4096 / sizeof(__u32))
-
-/**
- * struct adf_simple_buffer_allocate - request to allocate a "simple" buffer
- *
- * @w: width of buffer in pixels (input)
- * @h: height of buffer in pixels (input)
- * @format: DRM-style fourcc (input)
- *
- * @fd: dma_buf fd (output)
- * @offset: location of first pixel, in bytes (output)
- * @pitch: length of a scanline including padding, in bytes (output)
- *
- * Simple buffers are analogous to DRM's "dumb" buffers.  They have a single
- * plane of linear RGB data which can be allocated and scanned out without
- * any driver-private ioctls or data.
- *
- * @format must be a standard RGB format defined in drm_fourcc.h.
- *
- * ADF clients must NOT assume that an interface can scan out a simple buffer
- * allocated by a different ADF interface, even if the two interfaces belong to
- * the same ADF device.
- */
-struct adf_simple_buffer_alloc {
-	__u16 w;
-	__u16 h;
-	__u32 format;
-
-	__s32 fd;
-	__u32 offset;
-	__u32 pitch;
-};
-
-/**
- * struct adf_simple_post_config - request to flip to a single buffer without
- * driver-private data
- *
- * This request is equivalent to &struct adf_simple_post_config_v2 with
- * @complete_fence_type = %ADF_COMPLETE_FENCE_RELEASE.
- *
- * @buf: description of buffer displayed (input)
- * @complete_fence: sync_fence fd which will clear when this buffer has left the
- * screen (output)
- */
-struct adf_simple_post_config {
-	struct adf_buffer_config buf;
-	__s32 complete_fence;
-};
-
-/**
- * struct adf_simple_post_config_v2 - request to flip to a single buffer without
- * driver-private data
- *
- * @buf: description of buffer displayed (input)
- * @complete_fence_type: one of &enum adf_complete_fence_type describing what
- * 	fence to return (input)
- * @complete_fence: sync_fence fd which will fire at the time
- * 	requested by @complete_fence_type (output)
- */
-struct adf_simple_post_config_v2 {
-	struct adf_buffer_config buf;
-	__s32 complete_fence;
-	__u8 complete_fence_type;
-};
-
-/**
- * struct adf_attachment_config - description of attachment between an overlay
- * engine and an interface
- *
- * @overlay_engine: id of the overlay engine
- * @interface: id of the interface
- */
-struct adf_attachment_config {
-	__u32 overlay_engine;
-	__u32 interface;
-};
-
-/**
- * struct adf_device_data - describes a display device
- *
- * @name: display device's name
- * @n_attachments: the number of current attachments
- * @attachments: list of current attachments
- * @n_allowed_attachments: the number of allowed attachments
- * @allowed_attachments: list of allowed attachments
- * @custom_data_size: size of driver-private data
- * @custom_data: driver-private data
- */
-struct adf_device_data {
-	char name[ADF_NAME_LEN];
-
-	size_t n_attachments;
-	struct adf_attachment_config __user *attachments;
-
-	size_t n_allowed_attachments;
-	struct adf_attachment_config __user *allowed_attachments;
-
-	size_t custom_data_size;
-	void __user *custom_data;
-};
-#define ADF_MAX_ATTACHMENTS (4096 / sizeof(struct adf_attachment_config))
-
-/**
- * struct adf_device_data - describes a display interface
- *
- * @name: display interface's name
- * @type: interface type (see enum @adf_interface_type)
- * @id: which interface of type @type;
- *	e.g. interface DSI.1 -> @type=@ADF_INTF_TYPE_DSI, @id=1
- * @flags: informational flags (bitmask of %ADF_INTF_FLAG_* values)
- * @dpms_state: DPMS state (one of @DRM_MODE_DPMS_* defined in drm_mode.h)
- * @hotplug_detect: whether a display is plugged in
- * @width_mm: screen width in millimeters, or 0 if unknown
- * @height_mm: screen height in millimeters, or 0 if unknown
- * @current_mode: current display mode
- * @n_available_modes: the number of hardware display modes
- * @available_modes: list of hardware display modes
- * @custom_data_size: size of driver-private data
- * @custom_data: driver-private data
- */
-struct adf_interface_data {
-	char name[ADF_NAME_LEN];
-
-	__u32 type;
-	__u32 id;
-	/* e.g. type=ADF_INTF_TYPE_DSI, id=1 => DSI.1 */
-	__u32 flags;
-
-	__u8 dpms_state;
-	__u8 hotplug_detect;
-	__u16 width_mm;
-	__u16 height_mm;
-
-	struct drm_mode_modeinfo current_mode;
-	size_t n_available_modes;
-	struct drm_mode_modeinfo __user *available_modes;
-
-	size_t custom_data_size;
-	void __user *custom_data;
-};
-#define ADF_MAX_MODES (4096 / sizeof(struct drm_mode_modeinfo))
-
-/**
- * struct adf_overlay_engine_data - describes an overlay engine
- *
- * @name: overlay engine's name
- * @n_supported_formats: number of supported formats
- * @supported_formats: list of supported formats
- * @custom_data_size: size of driver-private data
- * @custom_data: driver-private data
- */
-struct adf_overlay_engine_data {
-	char name[ADF_NAME_LEN];
-
-	size_t n_supported_formats;
-	__u32 __user *supported_formats;
-
-	size_t custom_data_size;
-	void __user *custom_data;
-};
-#define ADF_MAX_SUPPORTED_FORMATS (4096 / sizeof(__u32))
-
-#define ADF_IOCTL_TYPE		'D'
-#define ADF_IOCTL_NR_CUSTOM	128
-
-#define ADF_SET_EVENT		_IOW(ADF_IOCTL_TYPE, 0, struct adf_set_event)
-#define ADF_BLANK		_IOW(ADF_IOCTL_TYPE, 1, __u8)
-#define ADF_POST_CONFIG		_IOW(ADF_IOCTL_TYPE, 2, struct adf_post_config)
-#define ADF_SET_MODE		_IOW(ADF_IOCTL_TYPE, 3, \
-					struct drm_mode_modeinfo)
-#define ADF_GET_DEVICE_DATA	_IOR(ADF_IOCTL_TYPE, 4, struct adf_device_data)
-#define ADF_GET_INTERFACE_DATA	_IOR(ADF_IOCTL_TYPE, 5, \
-					struct adf_interface_data)
-#define ADF_GET_OVERLAY_ENGINE_DATA \
-				_IOR(ADF_IOCTL_TYPE, 6, \
-					struct adf_overlay_engine_data)
-#define ADF_SIMPLE_POST_CONFIG	_IOW(ADF_IOCTL_TYPE, 7, \
-					struct adf_simple_post_config)
-#define ADF_SIMPLE_BUFFER_ALLOC	_IOW(ADF_IOCTL_TYPE, 8, \
-					struct adf_simple_buffer_alloc)
-#define ADF_ATTACH		_IOW(ADF_IOCTL_TYPE, 9, \
-					struct adf_attachment_config)
-#define ADF_DETACH		_IOW(ADF_IOCTL_TYPE, 10, \
-					struct adf_attachment_config)
-
-#define ADF_POST_CONFIG_V2	_IOW(ADF_IOCTL_TYPE, 11, \
-					struct adf_post_config_v2)
-#define ADF_SIMPLE_POST_CONFIG_V2 \
-				_IOW(ADF_IOCTL_TYPE, 12, \
-					struct adf_simple_post_config_v2)
-
-#endif /* _UAPI_VIDEO_ADF_H_ */
diff --git a/deprecated-adf/libadf/tests/adf_test.cpp b/deprecated-adf/libadf/tests/adf_test.cpp
deleted file mode 100644
index 82a91f4..0000000
--- a/deprecated-adf/libadf/tests/adf_test.cpp
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-
-#include <adf/adf.h>
-#include <gtest/gtest.h>
-#include <sys/mman.h>
-#include <sync/sync.h>
-
-class AdfTest : public testing::Test {
-public:
-    AdfTest() : intf_id(0), intf(-1), eng_id(0), eng(-1) { }
-
-    virtual void SetUp() {
-        int err = adf_device_open(dev_id, O_RDWR, &dev);
-        ASSERT_GE(err, 0) << "opening ADF device " << dev_id <<
-                " failed: " << strerror(-err);
-
-        err = adf_find_simple_post_configuration(&dev, fmt8888, n_fmt8888,
-                &intf_id, &eng_id);
-        ASSERT_GE(err, 0) << "finding ADF configuration failed: " <<
-                strerror(-err);
-
-        intf = adf_interface_open(&dev, intf_id, O_RDWR);
-        ASSERT_GE(intf, 0) << "opening ADF interface " << dev_id << "." <<
-                intf_id << " failed: " << strerror(-intf);
-
-        eng = adf_overlay_engine_open(&dev, eng_id, O_RDWR);
-        ASSERT_GE(eng, 0) << "opening ADF overlay engine " << dev_id << "." <<
-                eng_id << " failed: " << strerror(-eng);
-    }
-
-    virtual void TearDown() {
-        if (eng >= 0)
-            close(eng);
-        if (intf >= 0)
-            close(intf);
-        adf_device_close(&dev);
-    }
-
-    void get8888Format(uint32_t &fmt, char fmt_str[ADF_FORMAT_STR_SIZE]) {
-        adf_overlay_engine_data data;
-        int err = adf_get_overlay_engine_data(eng, &data);
-        ASSERT_GE(err, 0) << "getting ADF overlay engine data failed: " <<
-                strerror(-err);
-
-        for (size_t i = 0; i < data.n_supported_formats; i++) {
-            for (size_t j = 0; j < n_fmt8888; j++) {
-                if (data.supported_formats[i] == fmt8888[j]) {
-                    fmt = data.supported_formats[i];
-                    adf_format_str(fmt, fmt_str);
-                    adf_free_overlay_engine_data(&data);
-                    return;
-                }
-            }
-        }
-
-        adf_free_overlay_engine_data(&data);
-        FAIL(); /* this should never happen */
-    }
-
-    /* various helpers to call ADF and die on failure */
-
-    void getInterfaceData(adf_interface_data &data) {
-         int err = adf_get_interface_data(intf, &data);
-         ASSERT_GE(err, 0) << "getting ADF interface data failed: " <<
-                 strerror(-err);
-    }
-
-    void getCurrentMode(uint32_t &w, uint32_t &h) {
-        adf_interface_data data;
-        ASSERT_NO_FATAL_FAILURE(getInterfaceData(data));
-        w = data.current_mode.hdisplay;
-        h = data.current_mode.vdisplay;
-        adf_free_interface_data(&data);
-    }
-
-    void blank(uint8_t mode) {
-        int err = adf_interface_blank(intf, mode);
-        ASSERT_FALSE(err < 0 && err != -EBUSY) <<
-                "unblanking interface failed: " << strerror(-err);
-    }
-
-    void attach() {
-        int err = adf_device_attach(&dev, eng_id, intf_id);
-        ASSERT_FALSE(err < 0 && err != -EALREADY) <<
-                "attaching overlay engine " << eng_id << " to interface " <<
-                intf_id << " failed: " << strerror(-err);
-    }
-
-    void detach() {
-        int err = adf_device_detach(&dev, eng_id, intf_id);
-        ASSERT_FALSE(err < 0 && err != -EINVAL) <<
-                "detaching overlay engine " << eng_id << " from interface " <<
-                intf_id << " failed: " << strerror(-err);
-    }
-
-    void readVsyncTimestamp(uint64_t &timestamp) {
-        adf_event *event;
-        int err = adf_read_event(intf, &event);
-        ASSERT_GE(err, 0) << "reading ADF event failed: " << strerror(-err);
-
-        ASSERT_EQ(ADF_EVENT_VSYNC, event->type);
-        ASSERT_EQ(sizeof(adf_vsync_event), event->length);
-
-        adf_vsync_event *vsync_event =
-                reinterpret_cast<adf_vsync_event *>(event);
-        timestamp = vsync_event->timestamp;
-        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;
-    int intf;
-    adf_id_t eng_id;
-    int eng;
-
-private:
-    const static adf_id_t dev_id;
-    const static __u32 fmt8888[];
-    const static size_t n_fmt8888;
-};
-
-const adf_id_t AdfTest::dev_id = 0;
-
-const __u32 AdfTest::fmt8888[] = {
-   DRM_FORMAT_XRGB8888,
-   DRM_FORMAT_XBGR8888,
-   DRM_FORMAT_RGBX8888,
-   DRM_FORMAT_BGRX8888,
-   DRM_FORMAT_ARGB8888,
-   DRM_FORMAT_ABGR8888,
-   DRM_FORMAT_RGBA8888,
-   DRM_FORMAT_BGRA8888
-};
-const size_t AdfTest::n_fmt8888 = sizeof(fmt8888) / sizeof(fmt8888[0]);
-
-TEST(adf, devices) {
-    adf_id_t *devs = nullptr;
-    ssize_t n_devs = adf_devices(&devs);
-    free(devs);
-
-    ASSERT_GE(n_devs, 0) << "enumerating ADF devices failed: " <<
-            strerror(-n_devs);
-    ASSERT_TRUE(devs != NULL);
-}
-
-TEST_F(AdfTest, device_data) {
-    adf_device_data data;
-    int err = adf_get_device_data(&dev, &data);
-    ASSERT_GE(err, 0) << "getting ADF device data failed: " << strerror(-err);
-
-    EXPECT_LT(data.n_attachments, ADF_MAX_ATTACHMENTS);
-    EXPECT_GT(data.n_allowed_attachments, 0U);
-    EXPECT_LT(data.n_allowed_attachments, ADF_MAX_ATTACHMENTS);
-    EXPECT_LT(data.custom_data_size, (size_t)ADF_MAX_CUSTOM_DATA_SIZE);
-    adf_free_device_data(&data);
-}
-
-TEST_F(AdfTest, interface_data) {
-    adf_interface_data data;
-    ASSERT_NO_FATAL_FAILURE(getInterfaceData(data));
-
-    EXPECT_LT(data.type, ADF_INTF_TYPE_MAX);
-    EXPECT_LE(data.dpms_state, DRM_MODE_DPMS_OFF);
-    EXPECT_EQ(1, data.hotplug_detect);
-    EXPECT_GT(data.n_available_modes, 0U);
-    EXPECT_LT(data.custom_data_size, (size_t)ADF_MAX_CUSTOM_DATA_SIZE);
-    adf_free_interface_data(&data);
-}
-
-TEST_F(AdfTest, overlay_engine_data) {
-    adf_overlay_engine_data data;
-    int err = adf_get_overlay_engine_data(eng, &data);
-    ASSERT_GE(err, 0) << "getting ADF overlay engine failed: " <<
-            strerror(-err);
-
-    EXPECT_GT(data.n_supported_formats, 0U);
-    EXPECT_LT(data.n_supported_formats, ADF_MAX_SUPPORTED_FORMATS);
-    EXPECT_LT(data.custom_data_size, (size_t)ADF_MAX_CUSTOM_DATA_SIZE);
-    adf_free_overlay_engine_data(&data);
-}
-
-TEST_F(AdfTest, blank) {
-    int err = adf_interface_blank(intf, (uint8_t)-1);
-    EXPECT_EQ(-EINVAL, err) << "setting bogus DPMS mode should have failed";
-
-    err = adf_interface_blank(eng, DRM_MODE_DPMS_OFF);
-    EXPECT_EQ(-EINVAL, err) << "blanking overlay engine should have failed";
-
-    ASSERT_NO_FATAL_FAILURE(blank(DRM_MODE_DPMS_OFF));
-    err = adf_interface_blank(intf, DRM_MODE_DPMS_OFF);
-    EXPECT_EQ(-EBUSY, err) << "blanking interface twice should have failed";
-
-    ASSERT_NO_FATAL_FAILURE(blank(DRM_MODE_DPMS_ON));
-    err = adf_interface_blank(intf, DRM_MODE_DPMS_ON);
-    EXPECT_EQ(-EBUSY, err) << "unblanking interface twice should have failed";
-
-    adf_interface_data data;
-    ASSERT_NO_FATAL_FAILURE(getInterfaceData(data));
-    EXPECT_EQ(DRM_MODE_DPMS_ON, data.dpms_state);
-    adf_free_interface_data(&data);
-}
-
-TEST_F(AdfTest, event) {
-    int err = adf_set_event(intf, ADF_EVENT_TYPE_MAX, true);
-    EXPECT_EQ(-EINVAL, err) << "enabling bogus ADF event should have failed";
-
-    err = adf_set_event(intf, ADF_EVENT_TYPE_MAX, false);
-    EXPECT_EQ(-EINVAL, err) << "disabling bogus ADF event should have failed";
-
-    err = adf_set_event(intf, ADF_EVENT_VSYNC, true);
-    ASSERT_GE(err, 0) << "enabling vsync event failed: " << strerror(-err);
-
-    err = adf_set_event(intf, ADF_EVENT_VSYNC, true);
-    EXPECT_EQ(-EALREADY, err) <<
-            "enabling vsync event twice should have failed";
-
-    ASSERT_NO_FATAL_FAILURE(blank(DRM_MODE_DPMS_ON));
-
-    uint64_t timestamp1, timestamp2;
-    ASSERT_NO_FATAL_FAILURE(readVsyncTimestamp(timestamp1));
-    ASSERT_NO_FATAL_FAILURE(readVsyncTimestamp(timestamp2));
-    EXPECT_GT(timestamp2, timestamp1);
-
-    err = adf_set_event(intf, ADF_EVENT_VSYNC, false);
-    EXPECT_GE(err, 0) << "disabling vsync event failed: " << strerror(-err);
-
-    err = adf_set_event(intf, ADF_EVENT_VSYNC, false);
-    EXPECT_EQ(-EALREADY, err) <<
-            "disabling vsync event twice should have failed";
-}
-
-TEST_F(AdfTest, attach) {
-    ASSERT_NO_FATAL_FAILURE(attach());
-    int err = adf_device_attach(&dev, eng_id, intf_id);
-    EXPECT_EQ(-EALREADY, err) << "attaching overlay engine " << eng_id <<
-            " to interface " << intf_id << " twice should have failed";
-
-    ASSERT_NO_FATAL_FAILURE(detach());
-    err = adf_device_detach(&dev, eng_id, intf_id);
-    EXPECT_EQ(-EINVAL, err) << "detaching overlay engine " << eng_id <<
-            " from interface " << intf_id << " twice should have failed";
-
-    err = adf_device_attach(&dev, eng_id, ADF_MAX_INTERFACES);
-    EXPECT_EQ(-EINVAL, err) << "attaching overlay engine " << eng_id <<
-            " to bogus interface should have failed";
-
-    err = adf_device_detach(&dev, eng_id, ADF_MAX_INTERFACES);
-    EXPECT_EQ(-EINVAL, err) << "detaching overlay engine " << eng_id <<
-            " from bogus interface should have failed";
-}
-
-TEST_F(AdfTest, simple_buffer_alloc) {
-    uint32_t w = 0, h = 0;
-    ASSERT_NO_FATAL_FAILURE(getCurrentMode(w, h));
-
-    uint32_t format;
-    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);
-    EXPECT_GE(buf_fd, 0) << "allocating " << w << "x" << h << " " <<
-            format_str << " buffer failed: " << strerror(-buf_fd);
-    EXPECT_GE(pitch, w * 4);
-    close(buf_fd);
-
-    buf_fd = adf_interface_simple_buffer_alloc(intf, w, h, 0xDEADBEEF, &offset,
-            &pitch);
-    /* n.b.: ADF only allows simple buffers with built-in RGB formats,
-       so this should fail even if a driver supports custom format 0xDEADBEEF */
-    EXPECT_EQ(-EINVAL, buf_fd) <<
-            "allocating buffer with bogus format should have failed";
-}
-
-TEST_F(AdfTest, simple_buffer) {
-    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 release_fence = adf_interface_simple_post(intf, eng_id, w, h, format,
-            buf_fd, offset, pitch, -1);
-    close(buf_fd);
-    ASSERT_GE(release_fence, 0) << "posting " << w << "x" << h << " " <<
-            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/deprecated-adf/libadfhwc/Android.bp b/deprecated-adf/libadfhwc/Android.bp
deleted file mode 100644
index 57a8d76..0000000
--- a/deprecated-adf/libadfhwc/Android.bp
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-cc_library_static {
-    name: "libadfhwc",
-    srcs: ["adfhwc.cpp"],
-    static_libs: [
-        "libadf",
-        "liblog",
-        "libutils",
-    ],
-    cflags: [
-        "-DLOG_TAG=\"adfhwc\"",
-        "-Werror",
-    ],
-    local_include_dirs: ["include"],
-    export_include_dirs: ["include"],
-}
diff --git a/deprecated-adf/libadfhwc/adfhwc.cpp b/deprecated-adf/libadfhwc/adfhwc.cpp
deleted file mode 100644
index 63c0f75..0000000
--- a/deprecated-adf/libadfhwc/adfhwc.cpp
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <fcntl.h>
-#include <malloc.h>
-#include <poll.h>
-#include <pthread.h>
-#include <sys/resource.h>
-
-#include <log/log.h>
-#include <utils/Vector.h>
-
-#include <adf/adf.h>
-#include <adfhwc/adfhwc.h>
-
-struct adf_hwc_helper {
-    adf_hwc_event_callbacks const *event_cb;
-    void *event_cb_data;
-
-    pthread_t event_thread;
-
-    android::Vector<int> intf_fds;
-    android::Vector<drm_mode_modeinfo> display_configs;
-};
-
-template<typename T> inline T min(T a, T b) { return (a < b) ? a : b; }
-
-int adf_eventControl(struct adf_hwc_helper *dev, int disp, int event,
-        int enabled)
-{
-    if (enabled != !!enabled)
-        return -EINVAL;
-
-    if ((size_t)disp >= dev->intf_fds.size())
-        return -EINVAL;
-
-    switch (event) {
-    case HWC_EVENT_VSYNC:
-        return adf_set_event(dev->intf_fds[disp], ADF_EVENT_VSYNC, enabled);
-    }
-
-    return -EINVAL;
-}
-
-static inline int32_t dpi(uint16_t res, uint16_t size_mm)
-{
-    if (size_mm)
-        return 1000 * (res * 25.4f) / size_mm;
-    return 0;
-}
-
-int adf_blank(struct adf_hwc_helper *dev, int disp, int blank)
-{
-    if ((size_t)disp >= dev->intf_fds.size())
-        return -EINVAL;
-
-    uint8_t dpms_mode = blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON;
-    return adf_interface_blank(dev->intf_fds[disp], dpms_mode);
-}
-
-int adf_query_display_types_supported(struct adf_hwc_helper *dev, int *value)
-{
-    *value = 0;
-    if (dev->intf_fds.size() > 0)
-        *value |= HWC_DISPLAY_PRIMARY_BIT;
-    if (dev->intf_fds.size() > 1)
-        *value |= HWC_DISPLAY_EXTERNAL_BIT;
-
-    return 0;
-}
-
-int adf_getDisplayConfigs(struct adf_hwc_helper *dev, int disp,
-        uint32_t *configs, size_t *numConfigs)
-{
-    if ((size_t)disp >= dev->intf_fds.size())
-        return -EINVAL;
-
-    adf_interface_data data;
-    int err = adf_get_interface_data(dev->intf_fds[disp], &data);
-    if (err < 0) {
-        ALOGE("failed to get ADF interface data: %s", strerror(err));
-        return err;
-    }
-
-    if (!data.hotplug_detect)
-        return -ENODEV;
-
-    android::Vector<drm_mode_modeinfo *> unique_configs;
-    unique_configs.push_back(&data.current_mode);
-    for (size_t i = 0; i < data.n_available_modes; i++)
-        if (memcmp(&data.available_modes[i], &data.current_mode,
-                sizeof(data.current_mode)))
-            unique_configs.push_back(&data.available_modes[i]);
-
-    for (size_t i = 0; i < min(*numConfigs, unique_configs.size()); i++) {
-        configs[i] = dev->display_configs.size();
-        dev->display_configs.push_back(*unique_configs[i]);
-    }
-    *numConfigs = unique_configs.size();
-
-    adf_free_interface_data(&data);
-    return 0;
-}
-
-static int32_t adf_display_attribute(const adf_interface_data &data,
-        const drm_mode_modeinfo &mode, const uint32_t attribute)
-{
-    switch (attribute) {
-    case HWC_DISPLAY_VSYNC_PERIOD:
-        if (mode.vrefresh)
-            return 1000000000 / mode.vrefresh;
-        return 0;
-
-    case HWC_DISPLAY_WIDTH:
-        return mode.hdisplay;
-
-    case HWC_DISPLAY_HEIGHT:
-        return mode.vdisplay;
-
-    case HWC_DISPLAY_DPI_X:
-        return dpi(mode.hdisplay, data.width_mm);
-
-    case HWC_DISPLAY_DPI_Y:
-        return dpi(mode.vdisplay, data.height_mm);
-
-    default:
-        ALOGE("unknown display attribute %u", attribute);
-        return -EINVAL;
-    }
-}
-
-int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp,
-        uint32_t config, const uint32_t *attributes, int32_t *values)
-{
-    if ((size_t)disp >= dev->intf_fds.size())
-        return -EINVAL;
-
-    if (config >= dev->display_configs.size())
-        return -EINVAL;
-
-    adf_interface_data data;
-    int err = adf_get_interface_data(dev->intf_fds[disp], &data);
-    if (err < 0) {
-        ALOGE("failed to get ADF interface data: %s", strerror(err));
-        return err;
-    }
-
-    for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++)
-        values[i] = adf_display_attribute(data, dev->display_configs[config],
-                attributes[i]);
-
-    adf_free_interface_data(&data);
-    return 0;
-}
-
-static int32_t adf_display_attribute_hwc2(const adf_interface_data &data,
-        const drm_mode_modeinfo &mode, const uint32_t attribute)
-{
-    switch (attribute) {
-    case HWC2_ATTRIBUTE_VSYNC_PERIOD:
-        if (mode.vrefresh)
-            return 1000000000 / mode.vrefresh;
-        return 0;
-
-    case HWC2_ATTRIBUTE_WIDTH:
-        return mode.hdisplay;
-
-    case HWC2_ATTRIBUTE_HEIGHT:
-        return mode.vdisplay;
-
-    case HWC2_ATTRIBUTE_DPI_X:
-        return dpi(mode.hdisplay, data.width_mm);
-
-    case HWC2_ATTRIBUTE_DPI_Y:
-        return dpi(mode.vdisplay, data.height_mm);
-
-    default:
-        ALOGE("unknown display attribute %u", attribute);
-        return -EINVAL;
-    }
-}
-
-int adf_getDisplayAttributes_hwc2(struct adf_hwc_helper *dev, int disp,
-        uint32_t config, const uint32_t *attributes, int32_t *values)
-{
-    if ((size_t)disp >= dev->intf_fds.size())
-        return -EINVAL;
-
-    if (config >= dev->display_configs.size())
-        return -EINVAL;
-
-    adf_interface_data data;
-    int err = adf_get_interface_data(dev->intf_fds[disp], &data);
-    if (err < 0) {
-        ALOGE("failed to get ADF interface data: %s", strerror(err));
-        return err;
-    }
-
-    for (int i = 0; attributes[i] != HWC2_ATTRIBUTE_INVALID; i++)
-        values[i] = adf_display_attribute_hwc2(data,
-                dev->display_configs[config], attributes[i]);
-
-    adf_free_interface_data(&data);
-    return 0;
-}
-
-int adf_set_active_config_hwc2(struct adf_hwc_helper *dev, int disp,
-        uint32_t config)
-{
-    if ((size_t)disp >= dev->intf_fds.size())
-        return -EINVAL;
-
-    if (config >= dev->display_configs.size())
-        return -EINVAL;
-
-    struct drm_mode_modeinfo mode = dev->display_configs[config];
-
-    return adf_interface_set_mode(dev->intf_fds[disp], &mode);
-}
-
-static void handle_adf_event(struct adf_hwc_helper *dev, int disp)
-{
-    adf_event *event;
-    int err = adf_read_event(dev->intf_fds[disp], &event);
-    if (err < 0) {
-        ALOGE("error reading event from display %d: %s", disp, strerror(err));
-        return;
-    }
-
-    void *vsync_temp;
-    adf_vsync_event *vsync;
-    adf_hotplug_event *hotplug;
-
-    switch (event->type) {
-    case ADF_EVENT_VSYNC:
-        vsync_temp = event;
-        vsync = static_cast<adf_vsync_event *>(vsync_temp);
-        // casting directly to adf_vsync_event * makes g++ warn about
-        // potential alignment issues that don't apply here
-        dev->event_cb->vsync(dev->event_cb_data, disp, vsync->timestamp);
-        break;
-    case ADF_EVENT_HOTPLUG:
-        hotplug = reinterpret_cast<adf_hotplug_event *>(event);
-        dev->event_cb->hotplug(dev->event_cb_data, disp, hotplug->connected);
-        break;
-    default:
-        if (event->type < ADF_EVENT_DEVICE_CUSTOM)
-            ALOGW("unrecognized event type %u", event->type);
-        else if (!dev->event_cb || !dev->event_cb->custom_event)
-            ALOGW("unhandled event type %u", event->type);
-        else
-            dev->event_cb->custom_event(dev->event_cb_data, disp, event);
-    }
-    free(event);
-}
-
-static void *adf_event_thread(void *data)
-{
-    adf_hwc_helper *dev = static_cast<adf_hwc_helper *>(data);
-
-    setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
-
-    struct sigaction action = { };
-    sigemptyset(&action.sa_mask);
-    action.sa_flags = 0;
-    action.sa_handler = [](int) { pthread_exit(0); };
-
-    if (sigaction(SIGUSR2, &action, NULL) < 0) {
-        ALOGE("failed to set thread exit action %s", strerror(errno));
-        return NULL;
-    }
-
-    sigset_t signal_set;
-    sigemptyset(&signal_set);
-    sigaddset(&signal_set, SIGUSR2);
-
-    pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
-
-    pollfd fds[dev->intf_fds.size()];
-    for (size_t i = 0; i < dev->intf_fds.size(); i++) {
-        fds[i].fd = dev->intf_fds[i];
-        fds[i].events = POLLIN | POLLPRI;
-    }
-
-    while (true) {
-        if (TEMP_FAILURE_RETRY(poll(fds, dev->intf_fds.size(), -1)) < 0) {
-            ALOGE("error in event thread: %s", strerror(errno));
-            break;
-        }
-
-        for (size_t i = 0; i < dev->intf_fds.size(); i++)
-            if (fds[i].revents & (POLLIN | POLLPRI))
-                handle_adf_event(dev, i);
-    }
-
-    return NULL;
-}
-
-int adf_hwc_open(int *intf_fds, size_t n_intfs,
-        const struct adf_hwc_event_callbacks *event_cb, void *event_cb_data,
-        struct adf_hwc_helper **dev)
-{
-    if (!n_intfs)
-        return -EINVAL;
-
-    adf_hwc_helper *dev_ret = new adf_hwc_helper;
-    dev_ret->event_cb = event_cb;
-    dev_ret->event_cb_data = event_cb_data;
-
-    int ret;
-
-    for (size_t i = 0; i < n_intfs; i++) {
-        int dup_intf_fd = dup(intf_fds[i]);
-        if (dup_intf_fd < 0) {
-            ALOGE("failed to dup interface fd: %s", strerror(errno));
-            ret = -errno;
-            goto err;
-        }
-
-        dev_ret->intf_fds.push_back(dup_intf_fd);
-
-        ret = adf_set_event(dup_intf_fd, ADF_EVENT_HOTPLUG, 1);
-        if (ret < 0 && ret != -EINVAL) {
-            ALOGE("failed to enable hotplug event on display %zu: %s",
-                    i, strerror(errno));
-            goto err;
-        }
-    }
-
-    sigset_t signal_set;
-    sigemptyset(&signal_set);
-    sigaddset(&signal_set, SIGUSR2);
-
-    pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
-
-    ret = pthread_create(&dev_ret->event_thread, NULL, adf_event_thread,
-            dev_ret);
-    if (ret) {
-        ALOGE("failed to create event thread: %s", strerror(ret));
-        goto err;
-    }
-
-    *dev = dev_ret;
-    return 0;
-
-err:
-    for (size_t i = 0; i < dev_ret->intf_fds.size(); i++)
-        close(dev_ret->intf_fds[i]);
-
-    delete dev_ret;
-    return ret;
-}
-
-void adf_hwc_close(struct adf_hwc_helper *dev)
-{
-    pthread_kill(dev->event_thread, SIGUSR2);
-    pthread_join(dev->event_thread, NULL);
-
-    for (size_t i = 0; i < dev->intf_fds.size(); i++)
-        close(dev->intf_fds[i]);
-
-    delete dev;
-}
diff --git a/deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h b/deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h
deleted file mode 100644
index 4f70925..0000000
--- a/deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2013 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 _LIBADFHWC_ADFHWC_H_
-#define _LIBADFHWC_ADFHWC_H_
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <sys/cdefs.h>
-#include <video/adf.h>
-
-#include <hardware/hwcomposer.h>
-#include <hardware/hwcomposer2.h>
-
-struct adf_hwc_helper;
-
-struct adf_hwc_event_callbacks {
-    /**
-     * Called on vsync (required)
-     */
-    void (*vsync)(void *data, int disp, uint64_t timestamp);
-    /**
-     * Called on hotplug (required)
-     */
-    void (*hotplug)(void *data, int disp, bool connected);
-    /**
-     * Called on hardware-custom ADF events (optional)
-     */
-    void (*custom_event)(void *data, int disp, struct adf_event *event);
-};
-
-/**
- * Converts HAL pixel formats to equivalent ADF/DRM format FourCCs.
- */
-static inline uint32_t adf_fourcc_for_hal_pixel_format(int format)
-{
-    switch (format) {
-    case HAL_PIXEL_FORMAT_RGBA_8888:
-        return DRM_FORMAT_RGBA8888;
-    case HAL_PIXEL_FORMAT_RGBX_8888:
-        return DRM_FORMAT_RGBX8888;
-    case HAL_PIXEL_FORMAT_RGB_888:
-        return DRM_FORMAT_RGB888;
-    case HAL_PIXEL_FORMAT_RGB_565:
-        return DRM_FORMAT_RGB565;
-    case HAL_PIXEL_FORMAT_BGRA_8888:
-        return DRM_FORMAT_BGRA8888;
-    case HAL_PIXEL_FORMAT_YV12:
-        return DRM_FORMAT_YVU420;
-    case HAL_PIXEL_FORMAT_YCbCr_422_SP:
-        return DRM_FORMAT_NV16;
-    case HAL_PIXEL_FORMAT_YCrCb_420_SP:
-        return DRM_FORMAT_NV21;
-    case HAL_PIXEL_FORMAT_YCbCr_422_I:
-        return DRM_FORMAT_YUYV;
-    default:
-        return 0;
-    }
-}
-
-/**
- * Converts HAL display types to equivalent ADF interface flags.
- */
-static inline uint32_t adf_hwc_interface_flag_for_disp(int disp)
-{
-    switch (disp) {
-    case HWC_DISPLAY_PRIMARY:
-        return ADF_INTF_FLAG_PRIMARY;
-    case HWC_DISPLAY_EXTERNAL:
-        return ADF_INTF_FLAG_EXTERNAL;
-    default:
-        return 0;
-    }
-}
-
-__BEGIN_DECLS
-
-/**
- * Create a HWC helper for the specified ADF interfaces.
- *
- * intf_fds must be indexed by HWC display type: e.g.,
- * intf_fds[HWC_DISPLAY_PRIMARY] is the fd for the primary display
- * interface.  n_intfs must be >= 1.
- *
- * The caller retains ownership of the fds in intf_fds and must close()
- * them when they are no longer needed.
- *
- * On error, returns -errno.
- */
-int adf_hwc_open(int *intf_fds, size_t n_intfs,
-        const struct adf_hwc_event_callbacks *event_cb, void *event_cb_data,
-        struct adf_hwc_helper **dev);
-
-/**
- * Destroys a HWC helper.
- */
-void adf_hwc_close(struct adf_hwc_helper *dev);
-
-/**
- * Generic implementations of common HWC ops.
- *
- * The HWC should not point its ops directly at these helpers.  Instead, the HWC
- * should provide stub ops which call these helpers after converting the
- * hwc_composer_device_1* to a struct adf_hwc_helper*.
- */
-int adf_eventControl(struct adf_hwc_helper *dev, int disp, int event,
-        int enabled);
-int adf_blank(struct adf_hwc_helper *dev, int disp, int blank);
-int adf_query_display_types_supported(struct adf_hwc_helper *dev, int *value);
-int adf_getDisplayConfigs(struct adf_hwc_helper *dev, int disp,
-        uint32_t *configs, size_t *numConfigs);
-int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp,
-        uint32_t config, const uint32_t *attributes, int32_t *values);
-/**
- * Generic implementation of common HWC2 functions.
- *
- * The HWC2 should not return these functions directly through getFunction.
- * Instead, the HWC2 should return stub functions which call these helpers.
- */
-int adf_getDisplayAttributes_hwc2(struct adf_hwc_helper *dev, int disp,
-        uint32_t config, const uint32_t *attributes, int32_t *values);
-int adf_set_active_config_hwc2(struct adf_hwc_helper *dev, int disp,
-        uint32_t config);
-
-__END_DECLS
-
-#endif /* _LIBADFHWC_ADFHWC_H_ */
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
index 2088ae3..a72ee07 100644
--- a/fastboot/OWNERS
+++ b/fastboot/OWNERS
@@ -1,4 +1,4 @@
-dpursell@google.com
+dvander@google.com
+hridya@google.com
 enh@google.com
 jmgao@google.com
-tomcherry@google.com
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 4601960..2b2a0bf 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -228,6 +228,11 @@
         return device->WriteStatus(FastbootResult::FAIL, "Unable to open fastboot HAL");
     }
 
+    //Disable "oem postwipedata userdata" to prevent user wipe oem userdata only.
+    if (args[0] == "oem postwipedata userdata") {
+        return device->WriteStatus(FastbootResult::FAIL, "Unable to do oem postwipedata userdata");
+    }
+
     Result ret;
     auto ret_val = fastboot_hal->doOemCommand(args[0], [&](Result result) { ret = result; });
     if (!ret_val.isOk()) {
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash
index 406e8b8..f5a3384 100644
--- a/fastboot/fastboot.bash
+++ b/fastboot/fastboot.bash
@@ -109,7 +109,7 @@
 
     cur="${COMP_WORDS[COMP_CWORD]}"
     if [[ $i -eq $COMP_CWORD ]]; then
-        partitions="boot bootloader dtbo modem odm odm_dlkm oem product radio recovery system vbmeta vendor vendor_dlkm"
+        partitions="boot bootloader dtbo modem odm odm_dlkm oem product pvmfw radio recovery system vbmeta vendor vendor_dlkm"
         COMPREPLY=( $(compgen -W "$partitions" -- $cur) )
     else
         _fastboot_util_complete_local_file "${cur}" '!*.img'
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 62f6ac7..f7edf8e 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -147,6 +147,7 @@
     { "odm",      "odm.img",          "odm.sig",      "odm",      true,  ImageType::Normal },
     { "odm_dlkm", "odm_dlkm.img",     "odm_dlkm.sig", "odm_dlkm", true,  ImageType::Normal },
     { "product",  "product.img",      "product.sig",  "product",  true,  ImageType::Normal },
+    { "pvmfw",    "pvmfw.img",        "pvmfw.sig",    "pvmfw",    true,  ImageType::BootCritical },
     { "recovery", "recovery.img",     "recovery.sig", "recovery", true,  ImageType::BootCritical },
     { "super",    "super.img",        "super.sig",    "super",    true,  ImageType::Extra },
     { "system",   "system.img",       "system.sig",   "system",   false, ImageType::Normal },
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 8addcb6..458a7a1 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -168,6 +168,19 @@
     return exec_cmd(e2fsdroid_args[0], e2fsdroid_args.data(), nullptr);
 }
 
+enum {
+    // clang-format off
+    FSCK_SUCCESS                 = 0,
+    FSCK_ERROR_CORRECTED         = 1 << 0,
+    FSCK_SYSTEM_SHOULD_REBOOT    = 1 << 1,
+    FSCK_ERRORS_LEFT_UNCORRECTED = 1 << 2,
+    FSCK_OPERATIONAL_ERROR       = 1 << 3,
+    FSCK_USAGE_OR_SYNTAX_ERROR   = 1 << 4,
+    FSCK_USER_CANCELLED          = 1 << 5,
+    FSCK_SHARED_LIB_ERROR        = 1 << 7,
+    // clang-format on
+};
+
 static int generate_f2fs_image(const char* fileName, long long partSize,
                                const std::string& initial_dir, unsigned /* unused */,
                                unsigned /* unused */, const unsigned fsOptions) {
@@ -216,7 +229,11 @@
     std::vector<const char*> sload_args = {sload_path.c_str(), "-S",
                                        "-f", initial_dir.c_str(), fileName, nullptr};
 
-    return exec_cmd(sload_args[0], sload_args.data(), nullptr);
+    ret = exec_cmd(sload_args[0], sload_args.data(), nullptr);
+    if (ret != 0 && ret != FSCK_ERROR_CORRECTED) {
+        return -1;
+    }
+    return 0;
 }
 
 static const struct fs_generator {
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index ac784b2..96cc5c8 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -170,6 +170,7 @@
     defaults: ["fs_mgr_defaults"],
     static_libs: [
         "libavb_user",
+        "libgsid",
         "libutils",
         "libvold_binder",
     ],
diff --git a/fs_mgr/OWNERS b/fs_mgr/OWNERS
index cbbd3bc..cf353a1 100644
--- a/fs_mgr/OWNERS
+++ b/fs_mgr/OWNERS
@@ -1,3 +1,2 @@
 bowgotsai@google.com
 dvander@google.com
-tomcherry@google.com
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index ca782b9..94b2f8c 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -8,8 +8,8 @@
 system partition as read-write and then add or modify any number of files
 without reflashing the system image, which is efficient for a development cycle.
 
-Limited memory systems use read-only types of file systems or logical resizable
-Android partitions (LRAPs). These file systems land system partition images
+Limited memory systems use read-only types of file systems or dynamic
+Android partitions (DAPs). These file systems land system partition images
 right-sized, and have been deduped at the block level to compress the content.
 This means that a remount either isn’t possible, or isn't useful because of
 space limitations or support logistics.
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 6294b3f..2876094 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -656,7 +656,17 @@
 // If needed, we'll also enable (or disable) filesystem features as specified by
 // the fstab record.
 //
-static int prepare_fs_for_mount(const std::string& blk_device, const FstabEntry& entry) {
+static int prepare_fs_for_mount(const std::string& blk_device, const FstabEntry& entry,
+                                const std::string& alt_mount_point = "") {
+    auto& mount_point = alt_mount_point.empty() ? entry.mount_point : alt_mount_point;
+    // We need this because sometimes we have legacy symlinks that are
+    // lingering around and need cleaning up.
+    struct stat info;
+    if (lstat(mount_point.c_str(), &info) == 0 && (info.st_mode & S_IFMT) == S_IFLNK) {
+        unlink(mount_point.c_str());
+    }
+    mkdir(mount_point.c_str(), 0755);
+
     int fs_stat = 0;
 
     if (is_extfs(entry.fs_type)) {
@@ -684,7 +694,7 @@
 
     if (entry.fs_mgr_flags.check ||
         (fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) {
-        check_fs(blk_device, entry.fs_type, entry.mount_point, &fs_stat);
+        check_fs(blk_device, entry.fs_type, mount_point, &fs_stat);
     }
 
     if (is_extfs(entry.fs_type) &&
@@ -729,13 +739,6 @@
 // sets the underlying block device to read-only if the mount is read-only.
 // See "man 2 mount" for return values.
 static int __mount(const std::string& source, const std::string& target, const FstabEntry& entry) {
-    // We need this because sometimes we have legacy symlinks that are
-    // lingering around and need cleaning up.
-    struct stat info;
-    if (lstat(target.c_str(), &info) == 0 && (info.st_mode & S_IFMT) == S_IFLNK) {
-        unlink(target.c_str());
-    }
-    mkdir(target.c_str(), 0755);
     errno = 0;
     unsigned long mountflags = entry.flags;
     int ret = 0;
@@ -1321,6 +1324,7 @@
     int error_count = 0;
     CheckpointManager checkpoint_manager;
     AvbUniquePtr avb_handle(nullptr);
+    bool wiped = false;
 
     bool userdata_mounted = false;
     if (fstab->empty()) {
@@ -1454,7 +1458,8 @@
                 encryptable = status;
                 if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
                     if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.blk_device,
-                                   attempted_entry.mount_point},
+                                   attempted_entry.mount_point, wiped ? "true" : "false",
+                                   attempted_entry.fs_type},
                                   nullptr)) {
                         LERROR << "Encryption failed";
                         set_type_property(encryptable);
@@ -1471,7 +1476,7 @@
         }
 
         // Mounting failed, understand why and retry.
-        bool wiped = partition_wiped(current_entry.blk_device.c_str());
+        wiped = partition_wiped(current_entry.blk_device.c_str());
         bool crypt_footer = false;
         if (mount_errno != EBUSY && mount_errno != EACCES &&
             current_entry.fs_mgr_flags.formattable && wiped) {
@@ -1496,6 +1501,27 @@
             } else if (current_entry.is_encryptable() && current_entry.key_loc == KEY_IN_FOOTER) {
                 crypt_footer = true;
             }
+
+            // EncryptInplace will be used when vdc gives an error or needs to format partitions
+            // other than /data
+            if (should_use_metadata_encryption(current_entry) &&
+                current_entry.mount_point == "/data") {
+
+                // vdc->Format requires "ro.crypto.type" to set an encryption flag
+                encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
+                set_type_property(encryptable);
+
+                if (!call_vdc({"cryptfs", "encryptFstab", current_entry.blk_device,
+                               current_entry.mount_point, "true" /* shouldFormat */,
+                               current_entry.fs_type},
+                              nullptr)) {
+                    LERROR << "Encryption failed";
+                } else {
+                    userdata_mounted = true;
+                    continue;
+                }
+            }
+
             if (fs_mgr_do_format(current_entry, crypt_footer) == 0) {
                 // Let's replay the mount actions.
                 i = top_idx - 1;
@@ -1799,17 +1825,18 @@
 
 // wrapper to __mount() and expects a fully prepared fstab_rec,
 // unlike fs_mgr_do_mount which does more things with avb / verity etc.
-int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& mount_point) {
+int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& alt_mount_point) {
     // First check the filesystem if requested.
     if (entry.fs_mgr_flags.wait && !WaitForFile(entry.blk_device, 20s)) {
         LERROR << "Skipping mounting '" << entry.blk_device << "'";
     }
 
-    // Run fsck if needed
-    prepare_fs_for_mount(entry.blk_device, entry);
+    auto& mount_point = alt_mount_point.empty() ? entry.mount_point : alt_mount_point;
 
-    int ret =
-            __mount(entry.blk_device, mount_point.empty() ? entry.mount_point : mount_point, entry);
+    // Run fsck if needed
+    prepare_fs_for_mount(entry.blk_device, entry, mount_point);
+
+    int ret = __mount(entry.blk_device, mount_point, entry);
     if (ret) {
       ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
     }
@@ -1868,7 +1895,14 @@
             continue;
         }
 
-        int fs_stat = prepare_fs_for_mount(n_blk_device, fstab_entry);
+        // Now mount it where requested */
+        if (tmp_mount_point) {
+            mount_point = tmp_mount_point;
+        } else {
+            mount_point = fstab_entry.mount_point;
+        }
+
+        int fs_stat = prepare_fs_for_mount(n_blk_device, fstab_entry, mount_point);
 
         if (fstab_entry.fs_mgr_flags.avb) {
             if (!avb_handle) {
@@ -1902,12 +1936,6 @@
             }
         }
 
-        // Now mount it where requested */
-        if (tmp_mount_point) {
-            mount_point = tmp_mount_point;
-        } else {
-            mount_point = fstab_entry.mount_point;
-        }
         int retry_count = 2;
         while (retry_count-- > 0) {
             if (!__mount(n_blk_device, mount_point, fstab_entry)) {
@@ -1919,7 +1947,7 @@
                 mount_errors++;
                 fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
                 // try again after fsck
-                check_fs(n_blk_device, fstab_entry.fs_type, fstab_entry.mount_point, &fs_stat);
+                check_fs(n_blk_device, fstab_entry.fs_type, mount_point, &fs_stat);
             }
         }
         log_fs_stat(fstab_entry.blk_device, fs_stat);
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index b8b074e..745dab2 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -16,7 +16,6 @@
 
 #include <errno.h>
 #include <getopt.h>
-#include <libavb_user/libavb_user.h>
 #include <stdio.h>
 #include <sys/mount.h>
 #include <sys/types.h>
@@ -40,6 +39,8 @@
 #include <fs_mgr_overlayfs.h>
 #include <fs_mgr_priv.h>
 #include <fstab/fstab.h>
+#include <libavb_user/libavb_user.h>
+#include <libgsi/libgsid.h>
 
 namespace {
 
@@ -52,7 +53,9 @@
                  "\tpartition\tspecific partition(s) (empty does all)\n"
                  "\n"
                  "Remount specified partition(s) read-write, by name or mount point.\n"
-                 "-R notwithstanding, verity must be disabled on partition(s).";
+                 "-R notwithstanding, verity must be disabled on partition(s).\n"
+                 "-R within a DSU guest system reboots into the DSU instead of the host system,\n"
+                 "this command would enable DSU (one-shot) if not already enabled.";
 
     ::exit(exit_status);
 }
@@ -137,7 +140,8 @@
     REMOUNT_FAILED,
     MUST_REBOOT,
     BINDER_ERROR,
-    CHECKPOINTING
+    CHECKPOINTING,
+    GSID_ERROR,
 };
 
 static int do_remount(int argc, char* argv[]) {
@@ -340,6 +344,41 @@
         ++it;
     }
 
+    // If (1) remount requires a reboot to take effect, (2) system is currently
+    // running a DSU guest and (3) DSU is disabled, then enable DSU so that the
+    // next reboot would not take us back to the host system but stay within
+    // the guest system.
+    if (reboot_later) {
+        if (auto gsid = android::gsi::GetGsiService()) {
+            auto dsu_running = false;
+            if (auto status = gsid->isGsiRunning(&dsu_running); !status.isOk()) {
+                LOG(ERROR) << "Failed to get DSU running state: " << status;
+                return BINDER_ERROR;
+            }
+            auto dsu_enabled = false;
+            if (auto status = gsid->isGsiEnabled(&dsu_enabled); !status.isOk()) {
+                LOG(ERROR) << "Failed to get DSU enabled state: " << status;
+                return BINDER_ERROR;
+            }
+            if (dsu_running && !dsu_enabled) {
+                std::string dsu_slot;
+                if (auto status = gsid->getActiveDsuSlot(&dsu_slot); !status.isOk()) {
+                    LOG(ERROR) << "Failed to get active DSU slot: " << status;
+                    return BINDER_ERROR;
+                }
+                LOG(INFO) << "DSU is running but disabled, enable DSU so that we stay within the "
+                             "DSU guest system after reboot";
+                int error = 0;
+                if (auto status = gsid->enableGsi(/* oneShot = */ true, dsu_slot, &error);
+                    !status.isOk() || error != android::gsi::IGsiService::INSTALL_OK) {
+                    LOG(ERROR) << "Failed to enable DSU: " << status << ", error code: " << error;
+                    return !status.isOk() ? BINDER_ERROR : GSID_ERROR;
+                }
+                LOG(INFO) << "Successfully enabled DSU (one-shot mode)";
+            }
+        }
+    }
+
     if (partitions.empty() || just_disabled_verity) {
         if (reboot_later) reboot(setup_overlayfs);
         if (user_please_reboot_later) {
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
index 1e65587..fdaffbe 100644
--- a/fs_mgr/fs_mgr_roots.cpp
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -111,7 +111,8 @@
         return true;
     }
 
-    static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "f2fs", "none"};
+    static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "f2fs", "erofs",
+                                                       "none"};
     if (std::find(supported_fs.begin(), supported_fs.end(), rec->fs_type) == supported_fs.end()) {
         LERROR << "unknown fs_type \"" << rec->fs_type << "\" for " << mount_point;
         return false;
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index a0bc44d..0efe384 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -43,6 +43,7 @@
         },
     },
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
 }
 
 filegroup {
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index 93fc131..841f215 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -486,15 +486,14 @@
         if (!MapWithLoopDeviceList(loop_devices, name, timeout_ms, path)) {
             return false;
         }
+    } else {
+        auto status_message = "loop:" + loop_devices.back();
+        auto status_file = GetStatusFilePath(name);
+        if (!android::base::WriteStringToFile(status_message, status_file)) {
+            PLOG(ERROR) << "Write failed: " << status_file;
+            return false;
+        }
     }
-
-    auto status_message = "loop:" + loop_devices.back();
-    auto status_file = GetStatusFilePath(name);
-    if (!android::base::WriteStringToFile(status_message, status_file)) {
-        PLOG(ERROR) << "Write failed: " << status_file;
-        return false;
-    }
-
     auto_detach.Commit();
 
     *path = loop_devices.back();
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 910911e..d36a7f0 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -24,6 +24,7 @@
     ],
     shared_libs: [
         "libbase",
+        "libchrome",
         "libcutils",
         "liblog",
     ],
@@ -44,7 +45,6 @@
         "libz",
     ],
     header_libs: [
-        "libchrome",
         "libfiemap_headers",
         "libstorage_literals_headers",
         "libupdate_engine_headers",
@@ -174,6 +174,7 @@
         "libz",
     ],
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
 }
 
 cc_defaults {
@@ -350,6 +351,7 @@
     static_libs: [
         "libbase",
         "libbrotli",
+        "libchrome",
         "libcrypto_static",
         "libcutils",
         "libext2_uuid",
@@ -366,7 +368,6 @@
         "libz",
     ],
     header_libs: [
-        "libchrome",
         "libfiemap_headers",
         "libstorage_literals_headers",
         "libupdate_engine_headers",
@@ -408,9 +409,9 @@
         "fs_mgr_defaults",
     ],
     srcs: [
-	"snapuserd_server.cpp",
-    "snapuserd.cpp",
-	"snapuserd_daemon.cpp",
+        "snapuserd_server.cpp",
+        "snapuserd.cpp",
+        "snapuserd_daemon.cpp",
     ],
 
     cflags: [
@@ -421,11 +422,12 @@
     static_libs: [
         "libbase",
         "libbrotli",
-	"libcutils_sockets",
-        "liblog",
+        "libcutils_sockets",
         "libdm",
-        "libz",
+        "libgflags",
+        "liblog",
         "libsnapshot_cow",
+        "libz",
     ],
 }
 
@@ -436,15 +438,10 @@
         "snapuserd.rc",
     ],
     static_executable: true,
-}
-
-cc_binary {
-    name: "snapuserd_ramdisk",
-    stem: "snapuserd",
-    defaults: ["snapuserd_defaults"],
-
-    ramdisk: true,
-    static_executable: true,
+    system_shared_libs: [],
+    ramdisk_available: true,
+    vendor_ramdisk_available: true,
+    recovery_available: true,
 }
 
 cc_test {
@@ -568,7 +565,8 @@
         "libsnapshot_snapuserd",
         "libcutils_sockets",
         "libz",
-	"libdm",
+	"libfs_mgr",
+        "libdm",
     ],
     header_libs: [
         "libstorage_literals_headers",
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 0328132..36e1169 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -34,7 +34,19 @@
     MERGE_COMPLETED = 3;
 }
 
-// Next: 9
+// Next: 3
+enum MergePhase {
+    // No merge is in progress.
+    NO_MERGE = 0;
+
+    // Shrunk partitions can merge.
+    FIRST_PHASE = 1;
+
+    // Grown partitions can merge.
+    SECOND_PHASE = 2;
+}
+
+// Next: 11
 message SnapshotStatus {
     // Name of the snapshot. This is usually the name of the snapshotted
     // logical partition; for example, "system_b".
@@ -84,6 +96,12 @@
     // the merge process.
     // This is non-zero when |state| == MERGING or MERGE_COMPLETED.
     uint64 metadata_sectors = 8;
+
+    // True if compression is enabled, false otherwise.
+    bool compression_enabled = 9;
+
+    // The old partition size (if none existed, this will be zero).
+    uint64 old_partition_size = 10;
 }
 
 // Next: 8
@@ -115,7 +133,7 @@
     Cancelled = 7;
 };
 
-// Next: 5
+// Next: 7
 message SnapshotUpdateStatus {
     UpdateState state = 1;
 
@@ -130,6 +148,12 @@
 
     // Sectors allocated for metadata in all the snapshot devices.
     uint64 metadata_sectors = 4;
+
+    // Whether compression/dm-user was used for any snapshots.
+    bool compression_enabled = 5;
+
+    // Merge phase (if state == MERGING).
+    MergePhase merge_phase = 6;
 }
 
 // Next: 4
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
index 4db6584..a96352a 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -60,6 +60,7 @@
 
 TEST_F(CowTest, ReadWrite) {
     CowOptions options;
+    options.cluster_ops = 0;
     CowWriter writer(options);
 
     ASSERT_TRUE(writer.Initialize(cow_->fd));
@@ -137,6 +138,7 @@
 
 TEST_F(CowTest, CompressGz) {
     CowOptions options;
+    options.cluster_ops = 0;
     options.compression = "gz";
     CowWriter writer(options);
 
@@ -171,9 +173,74 @@
     ASSERT_TRUE(iter->Done());
 }
 
+TEST_F(CowTest, ClusterCompressGz) {
+    CowOptions options;
+    options.compression = "gz";
+    options.cluster_ops = 2;
+    CowWriter writer(options);
+
+    ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+
+    std::string data2 = "More data!";
+    data2.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer.AddRawBlocks(51, data2.data(), data2.size()));
+
+    ASSERT_TRUE(writer.Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->Done());
+    auto op = &iter->Get();
+
+    StringSink sink;
+
+    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->compression, kCowCompressGz);
+    ASSERT_EQ(op->data_length, 56);  // compressed!
+    ASSERT_EQ(op->new_block, 50);
+    ASSERT_TRUE(reader.ReadData(*op, &sink));
+    ASSERT_EQ(sink.stream(), data);
+
+    iter->Next();
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+
+    ASSERT_EQ(op->type, kCowClusterOp);
+
+    iter->Next();
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+
+    sink.Reset();
+    ASSERT_EQ(op->compression, kCowCompressGz);
+    ASSERT_EQ(op->data_length, 41);  // compressed!
+    ASSERT_EQ(op->new_block, 51);
+    ASSERT_TRUE(reader.ReadData(*op, &sink));
+    ASSERT_EQ(sink.stream(), data2);
+
+    iter->Next();
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+
+    ASSERT_EQ(op->type, kCowClusterOp);
+
+    iter->Next();
+    ASSERT_TRUE(iter->Done());
+}
+
 TEST_F(CowTest, CompressTwoBlocks) {
     CowOptions options;
     options.compression = "gz";
+    options.cluster_ops = 0;
     CowWriter writer(options);
 
     ASSERT_TRUE(writer.Initialize(cow_->fd));
@@ -216,6 +283,7 @@
 TEST_P(CompressionTest, HorribleSink) {
     CowOptions options;
     options.compression = GetParam();
+    options.cluster_ops = 0;
     CowWriter writer(options);
 
     ASSERT_TRUE(writer.Initialize(cow_->fd));
@@ -245,6 +313,7 @@
 
 TEST_F(CowTest, GetSize) {
     CowOptions options;
+    options.cluster_ops = 0;
     CowWriter writer(options);
     if (ftruncate(cow_->fd, 0) < 0) {
         perror("Fails to set temp file size");
@@ -270,6 +339,7 @@
 
 TEST_F(CowTest, AppendLabelSmall) {
     CowOptions options;
+    options.cluster_ops = 0;
     auto writer = std::make_unique<CowWriter>(options);
     ASSERT_TRUE(writer->Initialize(cow_->fd));
 
@@ -335,6 +405,7 @@
 
 TEST_F(CowTest, AppendLabelMissing) {
     CowOptions options;
+    options.cluster_ops = 0;
     auto writer = std::make_unique<CowWriter>(options);
     ASSERT_TRUE(writer->Initialize(cow_->fd));
 
@@ -388,6 +459,7 @@
 
 TEST_F(CowTest, AppendExtendedCorrupted) {
     CowOptions options;
+    options.cluster_ops = 0;
     auto writer = std::make_unique<CowWriter>(options);
     ASSERT_TRUE(writer->Initialize(cow_->fd));
 
@@ -440,6 +512,7 @@
 
 TEST_F(CowTest, AppendbyLabel) {
     CowOptions options;
+    options.cluster_ops = 0;
     auto writer = std::make_unique<CowWriter>(options);
     ASSERT_TRUE(writer->Initialize(cow_->fd));
 
@@ -525,6 +598,165 @@
     ASSERT_TRUE(iter->Done());
 }
 
+TEST_F(CowTest, ClusterTest) {
+    CowOptions options;
+    options.cluster_ops = 4;
+    auto writer = std::make_unique<CowWriter>(options);
+    ASSERT_TRUE(writer->Initialize(cow_->fd));
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+
+    ASSERT_TRUE(writer->AddLabel(4));
+
+    ASSERT_TRUE(writer->AddZeroBlocks(50, 2));  // Cluster split in middle
+
+    ASSERT_TRUE(writer->AddLabel(5));
+
+    ASSERT_TRUE(writer->AddCopy(5, 6));
+
+    // Cluster split
+
+    ASSERT_TRUE(writer->AddLabel(6));
+
+    ASSERT_TRUE(writer->Finalize());  // No data for cluster, so no cluster split needed
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    // Read back all ops
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    StringSink sink;
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+
+    ASSERT_FALSE(iter->Done());
+    auto op = &iter->Get();
+    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_TRUE(reader.ReadData(*op, &sink));
+    ASSERT_EQ(sink.stream(), data.substr(0, options.block_size));
+
+    iter->Next();
+    sink.Reset();
+
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+    ASSERT_EQ(op->type, kCowLabelOp);
+    ASSERT_EQ(op->source, 4);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+    ASSERT_EQ(op->type, kCowZeroOp);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+    ASSERT_EQ(op->type, kCowClusterOp);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+    ASSERT_EQ(op->type, kCowZeroOp);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+    ASSERT_EQ(op->type, kCowLabelOp);
+    ASSERT_EQ(op->source, 5);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+    ASSERT_EQ(op->type, kCowCopyOp);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+    ASSERT_EQ(op->type, kCowClusterOp);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+    ASSERT_EQ(op->type, kCowLabelOp);
+    ASSERT_EQ(op->source, 6);
+
+    iter->Next();
+
+    ASSERT_TRUE(iter->Done());
+}
+
+TEST_F(CowTest, ClusterAppendTest) {
+    CowOptions options;
+    options.cluster_ops = 3;
+    auto writer = std::make_unique<CowWriter>(options);
+    ASSERT_TRUE(writer->Initialize(cow_->fd));
+
+    ASSERT_TRUE(writer->AddLabel(50));
+    ASSERT_TRUE(writer->Finalize());  // Adds a cluster op, should be dropped on append
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    writer = std::make_unique<CowWriter>(options);
+    ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 50));
+
+    std::string data2 = "More data!";
+    data2.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
+    ASSERT_TRUE(writer->Finalize());  // Adds a cluster op
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    struct stat buf;
+    ASSERT_EQ(fstat(cow_->fd, &buf), 0);
+    ASSERT_EQ(buf.st_size, writer->GetCowSize());
+
+    // Read back both operations, plus cluster op at end
+    CowReader reader;
+    uint64_t label;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    ASSERT_TRUE(reader.GetLastLabel(&label));
+    ASSERT_EQ(label, 50);
+
+    StringSink sink;
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+
+    ASSERT_FALSE(iter->Done());
+    auto op = &iter->Get();
+    ASSERT_EQ(op->type, kCowLabelOp);
+    ASSERT_EQ(op->source, 50);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_TRUE(reader.ReadData(*op, &sink));
+    ASSERT_EQ(sink.stream(), data2);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->Done());
+    op = &iter->Get();
+    ASSERT_EQ(op->type, kCowClusterOp);
+
+    iter->Next();
+
+    ASSERT_TRUE(iter->Done());
+}
+
 }  // namespace snapshot
 }  // namespace android
 
diff --git a/fs_mgr/libsnapshot/cow_format.cpp b/fs_mgr/libsnapshot/cow_format.cpp
index 49ba11f..0753c49 100644
--- a/fs_mgr/libsnapshot/cow_format.cpp
+++ b/fs_mgr/libsnapshot/cow_format.cpp
@@ -35,6 +35,10 @@
         os << "kCowFooterOp,  ";
     else if (op.type == kCowLabelOp)
         os << "kCowLabelOp,   ";
+    else if (op.type == kCowClusterOp)
+        os << "kCowClusterOp  ";
+    else if (op.type == kCowFooterOp)
+        os << "kCowFooterOp  ";
     else
         os << (int)op.type << "?,";
     os << "compression:";
@@ -52,11 +56,35 @@
     return os;
 }
 
-int64_t GetNextOpOffset(const CowOperation& op) {
-    if (op.type == kCowReplaceOp)
+int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_ops) {
+    if (op.type == kCowClusterOp) {
+        return op.source;
+    } else if (op.type == kCowReplaceOp && cluster_ops == 0) {
         return op.data_length;
-    else
+    } else {
         return 0;
+    }
+}
+
+int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_ops) {
+    if (op.type == kCowClusterOp) {
+        return cluster_ops * sizeof(CowOperation);
+    } else if (cluster_ops == 0) {
+        return sizeof(CowOperation);
+    } else {
+        return 0;
+    }
+}
+
+bool IsMetadataOp(const CowOperation& op) {
+    switch (op.type) {
+        case kCowLabelOp:
+        case kCowClusterOp:
+        case kCowFooterOp:
+            return true;
+        default:
+            return false;
+    }
 }
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 6b7ada5..c15a05b 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -81,6 +81,24 @@
                    << sizeof(CowFooter);
         return false;
     }
+    if (header_.op_size != sizeof(CowOperation)) {
+        LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
+                   << sizeof(CowOperation);
+        return false;
+    }
+    if (header_.cluster_ops == 1) {
+        LOG(ERROR) << "Clusters must contain at least two operations to function.";
+        return false;
+    }
+    if (header_.op_size != sizeof(CowOperation)) {
+        LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
+                   << sizeof(CowOperation);
+        return false;
+    }
+    if (header_.cluster_ops == 1) {
+        LOG(ERROR) << "Clusters must contain at least two operations to function.";
+        return false;
+    }
 
     if ((header_.major_version != kCowVersionMajor) ||
         (header_.minor_version != kCowVersionMinor)) {
@@ -103,45 +121,64 @@
     }
 
     auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
+    uint64_t current_op_num = 0;
+    uint64_t cluster_ops = header_.cluster_ops ?: 1;
+    bool done = false;
 
-    // Alternating op and data
-    while (true) {
-        ops_buffer->emplace_back();
-        if (!android::base::ReadFully(fd_, &ops_buffer->back(), sizeof(CowOperation))) {
+    // Alternating op clusters and data
+    while (!done) {
+        uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperation));
+        if (to_add == 0) break;
+        ops_buffer->resize(current_op_num + to_add);
+        if (!android::base::ReadFully(fd_, &ops_buffer->data()[current_op_num],
+                                      to_add * sizeof(CowOperation))) {
             PLOG(ERROR) << "read op failed";
             return false;
         }
+        // Parse current cluster to find start of next cluster
+        while (current_op_num < ops_buffer->size()) {
+            auto& current_op = ops_buffer->data()[current_op_num];
+            current_op_num++;
+            pos += sizeof(CowOperation) + GetNextOpOffset(current_op, header_.cluster_ops);
 
-        auto& current_op = ops_buffer->back();
-        off_t offs = lseek(fd_.get(), GetNextOpOffset(current_op), SEEK_CUR);
-        if (offs < 0) {
+            if (current_op.type == kCowClusterOp) {
+                break;
+            } else if (current_op.type == kCowLabelOp) {
+                last_label_ = {current_op.source};
+
+                // If we reach the requested label, stop reading.
+                if (label && label.value() == current_op.source) {
+                    done = true;
+                    break;
+                }
+            } else if (current_op.type == kCowFooterOp) {
+                footer_.emplace();
+                CowFooter* footer = &footer_.value();
+                memcpy(&footer_->op, &current_op, sizeof(footer->op));
+                off_t offs = lseek(fd_.get(), pos, SEEK_SET);
+                if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
+                    PLOG(ERROR) << "lseek next op failed";
+                    return false;
+                }
+                if (!android::base::ReadFully(fd_, &footer->data, sizeof(footer->data))) {
+                    LOG(ERROR) << "Could not read COW footer";
+                    return false;
+                }
+
+                // Drop the footer from the op stream.
+                current_op_num--;
+                done = true;
+                break;
+            }
+        }
+
+        // Position for next cluster read
+        off_t offs = lseek(fd_.get(), pos, SEEK_SET);
+        if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
             PLOG(ERROR) << "lseek next op failed";
             return false;
         }
-        pos = static_cast<uint64_t>(offs);
-
-        if (current_op.type == kCowLabelOp) {
-            last_label_ = {current_op.source};
-
-            // If we reach the requested label, stop reading.
-            if (label && label.value() == current_op.source) {
-                break;
-            }
-        } else if (current_op.type == kCowFooterOp) {
-            footer_.emplace();
-
-            CowFooter* footer = &footer_.value();
-            memcpy(&footer_->op, &current_op, sizeof(footer->op));
-
-            if (!android::base::ReadFully(fd_, &footer->data, sizeof(footer->data))) {
-                LOG(ERROR) << "Could not read COW footer";
-                return false;
-            }
-
-            // Drop the footer from the op stream.
-            ops_buffer->pop_back();
-            break;
-        }
+        ops_buffer->resize(current_op_num);
     }
 
     // To successfully parse a COW file, we need either:
@@ -185,33 +222,138 @@
             LOG(ERROR) << "ops checksum does not match";
             return false;
         }
-    } else {
-        LOG(INFO) << "No COW Footer, recovered data";
-    }
-
-    if (header_.num_merge_ops > 0) {
-        uint64_t merge_ops = header_.num_merge_ops;
-        uint64_t metadata_ops = 0;
-        uint64_t current_op_num = 0;
-
-        CHECK(ops_buffer->size() >= merge_ops);
-        while (merge_ops) {
-            auto& current_op = ops_buffer->data()[current_op_num];
-            if (current_op.type == kCowLabelOp || current_op.type == kCowFooterOp) {
-                metadata_ops += 1;
-            } else {
-                merge_ops -= 1;
-            }
-            current_op_num += 1;
-        }
-        ops_buffer->erase(ops_buffer.get()->begin(),
-                          ops_buffer.get()->begin() + header_.num_merge_ops + metadata_ops);
     }
 
     ops_ = ops_buffer;
     return true;
 }
 
+void CowReader::InitializeMerge() {
+    uint64_t num_copy_ops = 0;
+
+    // Remove all the metadata operations
+    ops_->erase(std::remove_if(ops_.get()->begin(), ops_.get()->end(),
+                               [](CowOperation& op) { return IsMetadataOp(op); }),
+                ops_.get()->end());
+
+    // We will re-arrange the vector in such a way that
+    // kernel can batch merge. Ex:
+    //
+    // Existing COW format; All the copy operations
+    // are at the beginning.
+    // =======================================
+    // Copy-op-1    - cow_op->new_block = 1
+    // Copy-op-2    - cow_op->new_block = 2
+    // Copy-op-3    - cow_op->new_block = 3
+    // Replace-op-4 - cow_op->new_block = 6
+    // Replace-op-5 - cow_op->new_block = 4
+    // Replace-op-6 - cow_op->new_block = 8
+    // Replace-op-7 - cow_op->new_block = 9
+    // Zero-op-8    - cow_op->new_block = 7
+    // Zero-op-9    - cow_op->new_block = 5
+    // =======================================
+    //
+    // First find the operation which isn't a copy-op
+    // and then sort all the operations in descending order
+    // with the key being cow_op->new_block (source block)
+    //
+    // The data-structure will look like:
+    //
+    // =======================================
+    // Copy-op-1    - cow_op->new_block = 1
+    // Copy-op-2    - cow_op->new_block = 2
+    // Copy-op-3    - cow_op->new_block = 3
+    // Replace-op-7 - cow_op->new_block = 9
+    // Replace-op-6 - cow_op->new_block = 8
+    // Zero-op-8    - cow_op->new_block = 7
+    // Replace-op-4 - cow_op->new_block = 6
+    // Zero-op-9    - cow_op->new_block = 5
+    // Replace-op-5 - cow_op->new_block = 4
+    // =======================================
+    //
+    // Daemon will read the above data-structure in reverse-order
+    // when reading metadata. Thus, kernel will get the metadata
+    // in the following order:
+    //
+    // ========================================
+    // Replace-op-5 - cow_op->new_block = 4
+    // Zero-op-9    - cow_op->new_block = 5
+    // Replace-op-4 - cow_op->new_block = 6
+    // Zero-op-8    - cow_op->new_block = 7
+    // Replace-op-6 - cow_op->new_block = 8
+    // Replace-op-7 - cow_op->new_block = 9
+    // Copy-op-3    - cow_op->new_block = 3
+    // Copy-op-2    - cow_op->new_block = 2
+    // Copy-op-1    - cow_op->new_block = 1
+    // ===========================================
+    //
+    // When merging begins, kernel will start from the last
+    // metadata which was read: In the above format, Copy-op-1
+    // will be the first merge operation.
+    //
+    // Now, batching of the merge operations happens only when
+    // 1: origin block numbers in the base device are contiguous
+    // (cow_op->new_block) and,
+    // 2: cow block numbers which are assigned by daemon in ReadMetadata()
+    // are contiguous. These are monotonically increasing numbers.
+    //
+    // When both (1) and (2) are true, kernel will batch merge the operations.
+    // However, we do not want copy operations to be batch merged as
+    // a crash or system reboot during an overlapping copy can drive the device
+    // to a corrupted state. Hence, merging of copy operations should always be
+    // done as a individual 4k block. In the above case, since the
+    // cow_op->new_block numbers are contiguous, we will ensure that the
+    // cow block numbers assigned in ReadMetadata() for these respective copy
+    // operations are not contiguous forcing kernel to issue merge for each
+    // copy operations without batch merging.
+    //
+    // For all the other operations viz. Replace and Zero op, the cow block
+    // numbers assigned by daemon will be contiguous allowing kernel to batch
+    // merge.
+    //
+    // The final format after assiging COW block numbers by the daemon will
+    // look something like:
+    //
+    // =========================================================
+    // Replace-op-5 - cow_op->new_block = 4  cow-block-num = 2
+    // Zero-op-9    - cow_op->new_block = 5  cow-block-num = 3
+    // Replace-op-4 - cow_op->new_block = 6  cow-block-num = 4
+    // Zero-op-8    - cow_op->new_block = 7  cow-block-num = 5
+    // Replace-op-6 - cow_op->new_block = 8  cow-block-num = 6
+    // Replace-op-7 - cow_op->new_block = 9  cow-block-num = 7
+    // Copy-op-3    - cow_op->new_block = 3  cow-block-num = 9
+    // Copy-op-2    - cow_op->new_block = 2  cow-block-num = 11
+    // Copy-op-1    - cow_op->new_block = 1  cow-block-num = 13
+    // ==========================================================
+    //
+    // Merge sequence will look like:
+    //
+    // Merge-1 - Copy-op-1
+    // Merge-2 - Copy-op-2
+    // Merge-3 - Copy-op-3
+    // Merge-4 - Batch-merge {Replace-op-7, Replace-op-6, Zero-op-8,
+    //                        Replace-op-4, Zero-op-9, Replace-op-5 }
+    //==============================================================
+
+    for (uint64_t i = 0; i < ops_->size(); i++) {
+        auto& current_op = ops_->data()[i];
+        if (current_op.type != kCowCopyOp) {
+            break;
+        }
+        num_copy_ops += 1;
+    }
+
+    std::sort(ops_.get()->begin() + num_copy_ops, ops_.get()->end(),
+              [](CowOperation& op1, CowOperation& op2) -> bool {
+                  return op1.new_block > op2.new_block;
+              });
+
+    if (header_.num_merge_ops > 0) {
+        CHECK(ops_->size() >= header_.num_merge_ops);
+        ops_->erase(ops_.get()->begin(), ops_.get()->begin() + header_.num_merge_ops);
+    }
+}
+
 bool CowReader::GetHeader(CowHeader* header) {
     *header = header_;
     return true;
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
index 5483fd0..0addba3 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -12,10 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <fcntl.h>
 #include <linux/fs.h>
+#include <linux/memfd.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
+#include <sys/syscall.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include <chrono>
 #include <iostream>
@@ -24,7 +28,9 @@
 
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
+#include <fs_mgr/file_wait.h>
 #include <gtest/gtest.h>
+#include <libdm/dm.h>
 #include <libdm/loop_control.h>
 #include <libsnapshot/cow_writer.h>
 #include <libsnapshot/snapuserd_client.h>
@@ -37,428 +43,375 @@
 using android::base::unique_fd;
 using LoopDevice = android::dm::LoopDevice;
 using namespace std::chrono_literals;
+using namespace android::dm;
+using namespace std;
 
-class SnapuserdTest : public ::testing::Test {
-  protected:
-    void SetUp() override {
-        // TODO: Daemon started through first stage
-        // init does not have permission to read files
-        // from /data/nativetest.
-        system("setenforce 0");
-        cow_system_ = std::make_unique<TemporaryFile>();
-        ASSERT_GE(cow_system_->fd, 0) << strerror(errno);
+static constexpr char kSnapuserdSocketTest[] = "snapuserdTest";
 
-        cow_product_ = std::make_unique<TemporaryFile>();
-        ASSERT_GE(cow_product_->fd, 0) << strerror(errno);
+class TempDevice {
+  public:
+    TempDevice(const std::string& name, const DmTable& table)
+        : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
+        valid_ = dm_.CreateDevice(name, table, &path_, std::chrono::seconds(5));
+    }
+    TempDevice(TempDevice&& other) noexcept
+        : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
+        other.valid_ = false;
+    }
+    ~TempDevice() {
+        if (valid_) {
+            dm_.DeleteDevice(name_);
+        }
+    }
+    bool Destroy() {
+        if (!valid_) {
+            return false;
+        }
+        valid_ = false;
+        return dm_.DeleteDevice(name_);
+    }
+    const std::string& path() const { return path_; }
+    const std::string& name() const { return name_; }
+    bool valid() const { return valid_; }
 
-        cow_system_1_ = std::make_unique<TemporaryFile>();
-        ASSERT_GE(cow_system_1_->fd, 0) << strerror(errno);
+    TempDevice(const TempDevice&) = delete;
+    TempDevice& operator=(const TempDevice&) = delete;
 
-        cow_product_1_ = std::make_unique<TemporaryFile>();
-        ASSERT_GE(cow_product_1_->fd, 0) << strerror(errno);
-
-        std::string path = android::base::GetExecutableDirectory();
-
-        system_a_ = std::make_unique<TemporaryFile>(path);
-        ASSERT_GE(system_a_->fd, 0) << strerror(errno);
-
-        product_a_ = std::make_unique<TemporaryFile>(path);
-        ASSERT_GE(product_a_->fd, 0) << strerror(errno);
-
-        size_ = 100_MiB;
+    TempDevice& operator=(TempDevice&& other) noexcept {
+        name_ = other.name_;
+        valid_ = other.valid_;
+        other.valid_ = false;
+        return *this;
     }
 
-    void TearDown() override {
-        system("setenforce 1");
-        cow_system_ = nullptr;
-        cow_product_ = nullptr;
-
-        cow_system_1_ = nullptr;
-        cow_product_1_ = nullptr;
-    }
-
-    std::unique_ptr<TemporaryFile> system_a_;
-    std::unique_ptr<TemporaryFile> product_a_;
-
-    std::unique_ptr<LoopDevice> system_a_loop_;
-    std::unique_ptr<LoopDevice> product_a_loop_;
-
-    std::unique_ptr<TemporaryFile> cow_system_;
-    std::unique_ptr<TemporaryFile> cow_product_;
-
-    std::unique_ptr<TemporaryFile> cow_system_1_;
-    std::unique_ptr<TemporaryFile> cow_product_1_;
-
-    unique_fd sys_fd_;
-    unique_fd product_fd_;
-    size_t size_;
-
-    int system_blksize_;
-    int product_blksize_;
-    std::string system_device_name_;
-    std::string product_device_name_;
-
-    std::string system_device_ctrl_name_;
-    std::string product_device_ctrl_name_;
-
-    std::unique_ptr<uint8_t[]> random_buffer_1_;
-    std::unique_ptr<uint8_t[]> random_buffer_2_;
-    std::unique_ptr<uint8_t[]> zero_buffer_;
-    std::unique_ptr<uint8_t[]> system_buffer_;
-    std::unique_ptr<uint8_t[]> product_buffer_;
-
-    void Init();
-    void InitCowDevices();
-    void InitDaemon();
-    void CreateCowDevice(std::unique_ptr<TemporaryFile>& cow);
-    void CreateSystemDmUser(std::unique_ptr<TemporaryFile>& cow);
-    void CreateProductDmUser(std::unique_ptr<TemporaryFile>& cow);
-    void DeleteDmUser(std::unique_ptr<TemporaryFile>& cow, std::string snapshot_device);
-    void StartSnapuserdDaemon();
-    void CreateSnapshotDevices();
-    void SwitchSnapshotDevices();
-
-    std::string GetSystemControlPath() {
-        return std::string("/dev/dm-user/") + system_device_ctrl_name_;
-    }
-    std::string GetProductControlPath() {
-        return std::string("/dev/dm-user/") + product_device_ctrl_name_;
-    }
-
-    void TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>& buffer);
-    std::unique_ptr<SnapuserdClient> client_;
+  private:
+    DeviceMapper& dm_;
+    std::string name_;
+    std::string path_;
+    bool valid_;
 };
 
-void SnapuserdTest::Init() {
+class CowSnapuserdTest final {
+  public:
+    bool Setup();
+    bool Merge();
+    void ValidateMerge();
+    void ReadSnapshotDeviceAndValidate();
+    void Shutdown();
+
+    std::string snapshot_dev() const { return snapshot_dev_->path(); }
+
+    static const uint64_t kSectorSize = 512;
+
+  private:
+    void SetupImpl();
+    void MergeImpl();
+    void CreateCowDevice();
+    void CreateBaseDevice();
+    void InitCowDevice();
+    void SetDeviceControlName();
+    void InitDaemon();
+    void CreateDmUserDevice();
+    void StartSnapuserdDaemon();
+    void CreateSnapshotDevice();
+    unique_fd CreateTempFile(const std::string& name, size_t size);
+
+    unique_ptr<LoopDevice> base_loop_;
+    unique_ptr<TempDevice> dmuser_dev_;
+    unique_ptr<TempDevice> snapshot_dev_;
+
+    std::string system_device_ctrl_name_;
+    std::string system_device_name_;
+
+    unique_fd base_fd_;
+    std::unique_ptr<TemporaryFile> cow_system_;
+    std::unique_ptr<SnapuserdClient> client_;
+    std::unique_ptr<uint8_t[]> orig_buffer_;
+    std::unique_ptr<uint8_t[]> merged_buffer_;
+    bool setup_ok_ = false;
+    bool merge_ok_ = false;
+    size_t size_ = 1_MiB;
+    int cow_num_sectors_;
+    int total_base_size_;
+};
+
+unique_fd CowSnapuserdTest::CreateTempFile(const std::string& name, size_t size) {
+    unique_fd fd(syscall(__NR_memfd_create, name.c_str(), MFD_ALLOW_SEALING));
+    if (fd < 0) {
+        return {};
+    }
+    if (size) {
+        if (ftruncate(fd, size) < 0) {
+            perror("ftruncate");
+            return {};
+        }
+        if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
+            perror("fcntl");
+            return {};
+        }
+    }
+    return fd;
+}
+
+void CowSnapuserdTest::Shutdown() {
+    ASSERT_TRUE(client_->StopSnapuserd());
+    ASSERT_TRUE(snapshot_dev_->Destroy());
+    ASSERT_TRUE(dmuser_dev_->Destroy());
+}
+
+bool CowSnapuserdTest::Setup() {
+    SetupImpl();
+    return setup_ok_;
+}
+
+void CowSnapuserdTest::StartSnapuserdDaemon() {
+    pid_t pid = fork();
+    ASSERT_GE(pid, 0);
+    if (pid == 0) {
+        std::string arg0 = "/system/bin/snapuserd";
+        std::string arg1 = "-socket="s + kSnapuserdSocketTest;
+        char* const argv[] = {arg0.data(), arg1.data(), nullptr};
+        ASSERT_GE(execv(arg0.c_str(), argv), 0);
+    } else {
+        client_ = SnapuserdClient::Connect(kSnapuserdSocketTest, 10s);
+        ASSERT_NE(client_, nullptr);
+    }
+}
+
+void CowSnapuserdTest::CreateBaseDevice() {
     unique_fd rnd_fd;
-    loff_t offset = 0;
-    std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(1_MiB);
+
+    total_base_size_ = (size_ * 4);
+    base_fd_ = CreateTempFile("base_device", total_base_size_);
+    ASSERT_GE(base_fd_, 0);
 
     rnd_fd.reset(open("/dev/random", O_RDONLY));
     ASSERT_TRUE(rnd_fd > 0);
 
-    random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
-    random_buffer_2_ = std::make_unique<uint8_t[]>(size_);
-    system_buffer_ = std::make_unique<uint8_t[]>(size_);
-    product_buffer_ = std::make_unique<uint8_t[]>(size_);
-    zero_buffer_ = std::make_unique<uint8_t[]>(size_);
+    std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(1_MiB);
+
+    for (size_t j = 0; j < ((total_base_size_) / 1_MiB); j++) {
+        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true);
+        ASSERT_EQ(android::base::WriteFully(base_fd_, random_buffer.get(), 1_MiB), true);
+    }
+
+    ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0);
+
+    base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
+    ASSERT_TRUE(base_loop_->valid());
+}
+
+void CowSnapuserdTest::ReadSnapshotDeviceAndValidate() {
+    unique_fd snapshot_fd(open(snapshot_dev_->path().c_str(), O_RDONLY));
+    ASSERT_TRUE(snapshot_fd > 0);
+
+    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
+
+    // COPY
+    loff_t offset = 0;
+    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), size_), 0);
+
+    // REPLACE
+    offset += size_;
+    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + size_, size_), 0);
+
+    // ZERO
+    offset += size_;
+    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 2), size_), 0);
+
+    // REPLACE
+    offset += size_;
+    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 3), size_), 0);
+}
+
+void CowSnapuserdTest::CreateCowDevice() {
+    unique_fd rnd_fd;
+    loff_t offset = 0;
+
+    std::string path = android::base::GetExecutableDirectory();
+    cow_system_ = std::make_unique<TemporaryFile>(path);
+
+    rnd_fd.reset(open("/dev/random", O_RDONLY));
+    ASSERT_TRUE(rnd_fd > 0);
+
+    std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
 
     // Fill random data
     for (size_t j = 0; j < (size_ / 1_MiB); j++) {
         ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
                   true);
 
-        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_2_.get() + offset, 1_MiB, 0),
-                  true);
-
         offset += 1_MiB;
     }
 
-    for (size_t j = 0; j < (800_MiB / 1_MiB); j++) {
-        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true);
-        ASSERT_EQ(android::base::WriteFully(system_a_->fd, random_buffer.get(), 1_MiB), true);
-    }
-
-    for (size_t j = 0; j < (800_MiB / 1_MiB); j++) {
-        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true);
-        ASSERT_EQ(android::base::WriteFully(product_a_->fd, random_buffer.get(), 1_MiB), true);
-    }
-
-    // Create loopback devices
-    system_a_loop_ = std::make_unique<LoopDevice>(std::string(system_a_->path), 10s);
-    ASSERT_TRUE(system_a_loop_->valid());
-
-    product_a_loop_ = std::make_unique<LoopDevice>(std::string(product_a_->path), 10s);
-    ASSERT_TRUE(product_a_loop_->valid());
-
-    sys_fd_.reset(open(system_a_loop_->device().c_str(), O_RDONLY));
-    ASSERT_TRUE(sys_fd_ > 0);
-
-    product_fd_.reset(open(product_a_loop_->device().c_str(), O_RDONLY));
-    ASSERT_TRUE(product_fd_ > 0);
-
-    // Read from system partition from offset 0 of size 100MB
-    ASSERT_EQ(ReadFullyAtOffset(sys_fd_, system_buffer_.get(), size_, 0), true);
-
-    // Read from product partition from offset 0 of size 100MB
-    ASSERT_EQ(ReadFullyAtOffset(product_fd_, product_buffer_.get(), size_, 0), true);
-}
-
-void SnapuserdTest::CreateCowDevice(std::unique_ptr<TemporaryFile>& cow) {
-    //================Create a COW file with the following operations===========
-    //
-    // Create COW file which is gz compressed
-    //
-    // 0-100 MB of replace operation with random data
-    // 100-200 MB of copy operation
-    // 200-300 MB of zero operation
-    // 300-400 MB of replace operation with random data
-
     CowOptions options;
     options.compression = "gz";
     CowWriter writer(options);
 
-    ASSERT_TRUE(writer.Initialize(cow->fd));
-
-    // Write 100MB random data to COW file which is gz compressed from block 0
-    ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1_.get(), size_));
+    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
 
     size_t num_blocks = size_ / options.block_size;
-    size_t blk_start_copy = num_blocks;
-    size_t blk_end_copy = blk_start_copy + num_blocks;
-    size_t source_blk = 0;
+    size_t blk_end_copy = num_blocks * 2;
+    size_t source_blk = num_blocks - 1;
+    size_t blk_src_copy = blk_end_copy - 1;
 
-    // Copy blocks - source_blk starts from 0 as snapuserd
-    // has to read from block 0 in system_a partition
-    //
-    // This initializes copy operation from block 0 of size 100 MB from
-    // /dev/block/mapper/system_a or product_a
-    for (size_t i = blk_start_copy; i < blk_end_copy; i++) {
-        ASSERT_TRUE(writer.AddCopy(i, source_blk));
-        source_blk += 1;
+    size_t x = num_blocks;
+    while (1) {
+        ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+        x -= 1;
+        if (x == 0) {
+            break;
+        }
+        source_blk -= 1;
+        blk_src_copy -= 1;
     }
 
-    size_t blk_zero_copy_start = blk_end_copy;
+    source_blk = num_blocks;
+    blk_src_copy = blk_end_copy;
+
+    ASSERT_TRUE(writer.AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
+
+    size_t blk_zero_copy_start = source_blk + num_blocks;
     size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks;
 
-    // 100 MB filled with zeroes
     ASSERT_TRUE(writer.AddZeroBlocks(blk_zero_copy_start, num_blocks));
 
-    // Final 100MB filled with random data which is gz compressed
     size_t blk_random2_replace_start = blk_zero_copy_end;
 
-    ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_2_.get(), size_));
+    ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
 
     // Flush operations
     ASSERT_TRUE(writer.Finalize());
-
-    ASSERT_EQ(lseek(cow->fd, 0, SEEK_SET), 0);
+    // Construct the buffer required for validation
+    orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
+    std::string zero_buffer(size_, 0);
+    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), size_, size_), true);
+    memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_);
+    memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_);
+    memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_1_.get(), size_);
 }
 
-void SnapuserdTest::CreateSystemDmUser(std::unique_ptr<TemporaryFile>& cow) {
-    std::string cmd;
+void CowSnapuserdTest::InitCowDevice() {
+    cow_num_sectors_ = client_->InitDmUserCow(system_device_ctrl_name_, cow_system_->path,
+                                              base_loop_->device());
+    ASSERT_NE(cow_num_sectors_, 0);
+}
+
+void CowSnapuserdTest::SetDeviceControlName() {
     system_device_name_.clear();
     system_device_ctrl_name_.clear();
 
-    std::string str(cow->path);
+    std::string str(cow_system_->path);
     std::size_t found = str.find_last_of("/\\");
     ASSERT_NE(found, std::string::npos);
     system_device_name_ = str.substr(found + 1);
 
-    // Create a control device
     system_device_ctrl_name_ = system_device_name_ + "-ctrl";
-    cmd = "dmctl create " + system_device_name_ + " user 0 " + std::to_string(system_blksize_);
-    cmd += " " + system_device_ctrl_name_;
-
-    system(cmd.c_str());
 }
 
-void SnapuserdTest::DeleteDmUser(std::unique_ptr<TemporaryFile>& cow, std::string snapshot_device) {
-    std::string cmd;
+void CowSnapuserdTest::CreateDmUserDevice() {
+    DmTable dmuser_table;
+    ASSERT_TRUE(dmuser_table.AddTarget(
+            std::make_unique<DmTargetUser>(0, cow_num_sectors_, system_device_ctrl_name_)));
+    ASSERT_TRUE(dmuser_table.valid());
 
-    cmd = "dmctl delete " + snapshot_device;
-    system(cmd.c_str());
+    dmuser_dev_ = std::make_unique<TempDevice>(system_device_name_, dmuser_table);
+    ASSERT_TRUE(dmuser_dev_->valid());
+    ASSERT_FALSE(dmuser_dev_->path().empty());
 
-    cmd.clear();
-
-    std::string str(cow->path);
-    std::size_t found = str.find_last_of("/\\");
-    ASSERT_NE(found, std::string::npos);
-    std::string device_name = str.substr(found + 1);
-
-    cmd = "dmctl delete " + device_name;
-
-    system(cmd.c_str());
+    auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
+    ASSERT_TRUE(android::fs_mgr::WaitForFile(misc_device, 10s));
 }
 
-void SnapuserdTest::CreateProductDmUser(std::unique_ptr<TemporaryFile>& cow) {
-    std::string cmd;
-    product_device_name_.clear();
-    product_device_ctrl_name_.clear();
-
-    std::string str(cow->path);
-    std::size_t found = str.find_last_of("/\\");
-    ASSERT_NE(found, std::string::npos);
-    product_device_name_ = str.substr(found + 1);
-    product_device_ctrl_name_ = product_device_name_ + "-ctrl";
-    cmd = "dmctl create " + product_device_name_ + " user 0 " + std::to_string(product_blksize_);
-    cmd += " " + product_device_ctrl_name_;
-
-    system(cmd.c_str());
-}
-
-void SnapuserdTest::InitCowDevices() {
-    system_blksize_ = client_->InitDmUserCow(system_device_ctrl_name_, cow_system_->path,
-                                             system_a_loop_->device());
-    ASSERT_NE(system_blksize_, 0);
-
-    product_blksize_ = client_->InitDmUserCow(product_device_ctrl_name_, cow_product_->path,
-                                              product_a_loop_->device());
-    ASSERT_NE(product_blksize_, 0);
-}
-
-void SnapuserdTest::InitDaemon() {
+void CowSnapuserdTest::InitDaemon() {
     bool ok = client_->AttachDmUser(system_device_ctrl_name_);
     ASSERT_TRUE(ok);
-
-    ok = client_->AttachDmUser(product_device_ctrl_name_);
-    ASSERT_TRUE(ok);
 }
 
-void SnapuserdTest::StartSnapuserdDaemon() {
-    ASSERT_TRUE(EnsureSnapuserdStarted());
+void CowSnapuserdTest::CreateSnapshotDevice() {
+    DmTable snap_table;
+    ASSERT_TRUE(snap_table.AddTarget(std::make_unique<DmTargetSnapshot>(
+            0, total_base_size_ / kSectorSize, base_loop_->device(), dmuser_dev_->path(),
+            SnapshotStorageMode::Persistent, 8)));
+    ASSERT_TRUE(snap_table.valid());
 
-    client_ = SnapuserdClient::Connect(kSnapuserdSocket, 5s);
-    ASSERT_NE(client_, nullptr);
+    snap_table.set_readonly(true);
+
+    snapshot_dev_ = std::make_unique<TempDevice>("cowsnapuserd-test-dm-snapshot", snap_table);
+    ASSERT_TRUE(snapshot_dev_->valid());
+    ASSERT_FALSE(snapshot_dev_->path().empty());
 }
 
-void SnapuserdTest::CreateSnapshotDevices() {
-    std::string cmd;
+void CowSnapuserdTest::SetupImpl() {
+    CreateBaseDevice();
+    CreateCowDevice();
 
-    cmd = "dmctl create system-snapshot -ro snapshot 0 " + std::to_string(system_blksize_);
-    cmd += " " + system_a_loop_->device();
-    cmd += " /dev/block/mapper/" + system_device_name_;
-    cmd += " P 8";
-
-    system(cmd.c_str());
-
-    cmd.clear();
-
-    cmd = "dmctl create product-snapshot -ro snapshot 0 " + std::to_string(product_blksize_);
-    cmd += " " + product_a_loop_->device();
-    cmd += " /dev/block/mapper/" + product_device_name_;
-    cmd += " P 8";
-
-    system(cmd.c_str());
-}
-
-void SnapuserdTest::SwitchSnapshotDevices() {
-    std::string cmd;
-
-    cmd = "dmctl create system-snapshot-1 -ro snapshot 0 " + std::to_string(system_blksize_);
-    cmd += " " + system_a_loop_->device();
-    cmd += " /dev/block/mapper/" + system_device_name_;
-    cmd += " P 8";
-
-    system(cmd.c_str());
-
-    cmd.clear();
-
-    cmd = "dmctl create product-snapshot-1 -ro snapshot 0 " + std::to_string(product_blksize_);
-    cmd += " " + product_a_loop_->device();
-    cmd += " /dev/block/mapper/" + product_device_name_;
-    cmd += " P 8";
-
-    system(cmd.c_str());
-}
-
-void SnapuserdTest::TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>& buffer) {
-    loff_t offset = 0;
-    // std::unique_ptr<uint8_t[]> buffer = std::move(buf);
-
-    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
-
-    //================Start IO operation on dm-snapshot device=================
-    // This will test the following paths:
-    //
-    // 1: IO path for all three operations and interleaving of operations.
-    // 2: Merging of blocks in kernel during metadata read
-    // 3: Bulk IO issued by kernel duing merge operation
-
-    // Read from snapshot device of size 100MB from offset 0. This tests the
-    // 1st replace operation.
-    //
-    // IO path:
-    //
-    // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_compressed_cow (replace
-    // op)->decompress_cow->return
-
-    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
-
-    // Update the offset
-    offset += size_;
-
-    // Compare data with random_buffer_1_.
-    ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_1_.get(), size_), 0);
-
-    // Clear the buffer
-    memset(snapuserd_buffer.get(), 0, size_);
-
-    // Read from snapshot device of size 100MB from offset 100MB. This tests the
-    // copy operation.
-    //
-    // IO path:
-    //
-    // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_from_(system_a/product_a) partition
-    // (copy op) -> return
-    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
-
-    // Update the offset
-    offset += size_;
-
-    // Compare data with buffer.
-    ASSERT_EQ(memcmp(snapuserd_buffer.get(), buffer.get(), size_), 0);
-
-    // Read from snapshot device of size 100MB from offset 200MB. This tests the
-    // zero operation.
-    //
-    // IO path:
-    //
-    // dm-snap->dm-snap-persistent->dm-user->snapuserd->fill_memory_with_zero
-    // (zero op) -> return
-    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
-
-    // Compare data with zero filled buffer
-    ASSERT_EQ(memcmp(snapuserd_buffer.get(), zero_buffer_.get(), size_), 0);
-
-    // Update the offset
-    offset += size_;
-
-    // Read from snapshot device of size 100MB from offset 300MB. This tests the
-    // final replace operation.
-    //
-    // IO path:
-    //
-    // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_compressed_cow (replace
-    // op)->decompress_cow->return
-    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
-
-    // Compare data with random_buffer_2_.
-    ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_2_.get(), size_), 0);
-}
-
-TEST_F(SnapuserdTest, ReadWrite) {
-    unique_fd snapshot_fd;
-
-    Init();
-
-    CreateCowDevice(cow_system_);
-    CreateCowDevice(cow_product_);
+    SetDeviceControlName();
 
     StartSnapuserdDaemon();
-    InitCowDevices();
+    InitCowDevice();
 
-    CreateSystemDmUser(cow_system_);
-    CreateProductDmUser(cow_product_);
-
+    CreateDmUserDevice();
     InitDaemon();
 
-    CreateSnapshotDevices();
+    CreateSnapshotDevice();
+    setup_ok_ = true;
+}
 
-    snapshot_fd.reset(open("/dev/block/mapper/system-snapshot", O_RDONLY));
-    ASSERT_TRUE(snapshot_fd > 0);
-    TestIO(snapshot_fd, system_buffer_);
+bool CowSnapuserdTest::Merge() {
+    MergeImpl();
+    return merge_ok_;
+}
 
-    snapshot_fd.reset(open("/dev/block/mapper/product-snapshot", O_RDONLY));
-    ASSERT_TRUE(snapshot_fd > 0);
-    TestIO(snapshot_fd, product_buffer_);
+void CowSnapuserdTest::MergeImpl() {
+    DmTable merge_table;
+    ASSERT_TRUE(merge_table.AddTarget(std::make_unique<DmTargetSnapshot>(
+            0, total_base_size_ / kSectorSize, base_loop_->device(), dmuser_dev_->path(),
+            SnapshotStorageMode::Merge, 8)));
+    ASSERT_TRUE(merge_table.valid());
+    ASSERT_EQ(total_base_size_ / kSectorSize, merge_table.num_sectors());
 
-    snapshot_fd.reset(-1);
+    DeviceMapper& dm = DeviceMapper::Instance();
+    ASSERT_TRUE(dm.LoadTableAndActivate("cowsnapuserd-test-dm-snapshot", merge_table));
 
-    DeleteDmUser(cow_system_, "system-snapshot");
-    DeleteDmUser(cow_product_, "product-snapshot");
+    while (true) {
+        vector<DeviceMapper::TargetInfo> status;
+        ASSERT_TRUE(dm.GetTableStatus("cowsnapuserd-test-dm-snapshot", &status));
+        ASSERT_EQ(status.size(), 1);
+        ASSERT_EQ(strncmp(status[0].spec.target_type, "snapshot-merge", strlen("snapshot-merge")),
+                  0);
 
-    ASSERT_TRUE(client_->StopSnapuserd());
+        DmTargetSnapshot::Status merge_status;
+        ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(status[0].data, &merge_status));
+        ASSERT_TRUE(merge_status.error.empty());
+        if (merge_status.sectors_allocated == merge_status.metadata_sectors) {
+            break;
+        }
+
+        std::this_thread::sleep_for(250ms);
+    }
+
+    merge_ok_ = true;
+}
+
+void CowSnapuserdTest::ValidateMerge() {
+    merged_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
+    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, merged_buffer_.get(), total_base_size_, 0),
+              true);
+    ASSERT_EQ(memcmp(merged_buffer_.get(), orig_buffer_.get(), total_base_size_), 0);
+}
+
+TEST(Snapuserd_Test, Snapshot) {
+    CowSnapuserdTest harness;
+    ASSERT_TRUE(harness.Setup());
+    harness.ReadSnapshotDeviceAndValidate();
+    ASSERT_TRUE(harness.Merge());
+    harness.ValidateMerge();
+    harness.Shutdown();
 }
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 957ba35..c1a5f32 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -90,8 +90,10 @@
     header_.minor_version = kCowVersionMinor;
     header_.header_size = sizeof(CowHeader);
     header_.footer_size = sizeof(CowFooter);
+    header_.op_size = sizeof(CowOperation);
     header_.block_size = options_.block_size;
     header_.num_merge_ops = 0;
+    header_.cluster_ops = options_.cluster_ops;
     footer_ = {};
     footer_.op.data_length = 64;
     footer_.op.type = kCowFooterOp;
@@ -108,6 +110,10 @@
         LOG(ERROR) << "unrecognized compression: " << options_.compression;
         return false;
     }
+    if (options_.cluster_ops == 1) {
+        LOG(ERROR) << "Clusters must contain at least two operations to function.";
+        return false;
+    }
     return true;
 }
 
@@ -165,6 +171,19 @@
     return OpenForAppend(label);
 }
 
+void CowWriter::InitPos() {
+    next_op_pos_ = sizeof(header_);
+    cluster_size_ = header_.cluster_ops * sizeof(CowOperation);
+    if (header_.cluster_ops) {
+        next_data_pos_ = next_op_pos_ + cluster_size_;
+    } else {
+        next_data_pos_ = next_op_pos_ + sizeof(CowOperation);
+    }
+    ops_.clear();
+    current_cluster_size_ = 0;
+    current_data_size_ = 0;
+}
+
 bool CowWriter::OpenForWrite() {
     // This limitation is tied to the data field size in CowOperation.
     if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
@@ -184,7 +203,7 @@
         return false;
     }
 
-    next_op_pos_ = sizeof(header_);
+    InitPos();
     return true;
 }
 
@@ -197,13 +216,14 @@
     }
 
     options_.block_size = header_.block_size;
+    options_.cluster_ops = header_.cluster_ops;
 
     // Reset this, since we're going to reimport all operations.
     footer_.op.num_ops = 0;
-    next_op_pos_ = sizeof(header_);
-    ops_.resize(0);
+    InitPos();
 
     auto iter = reader->GetOpIter();
+
     while (!iter->Done()) {
         AddOperation(iter->Get());
         iter->Next();
@@ -234,14 +254,12 @@
 
 bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
     const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
-    uint64_t pos;
     CHECK(!merge_in_progress_);
     for (size_t i = 0; i < size / header_.block_size; i++) {
         CowOperation op = {};
         op.type = kCowReplaceOp;
         op.new_block = new_block_start + i;
-        GetDataPos(&pos);
-        op.source = pos + sizeof(op);
+        op.source = next_data_pos_;
 
         if (compression_) {
             auto data = Compress(iter, header_.block_size);
@@ -293,6 +311,14 @@
     return WriteOperation(op) && Sync();
 }
 
+bool CowWriter::EmitCluster() {
+    CowOperation op = {};
+    op.type = kCowClusterOp;
+    // Next cluster starts after remainder of current cluster and the next data block.
+    op.source = current_data_size_ + cluster_size_ - current_cluster_size_ - sizeof(CowOperation);
+    return WriteOperation(op);
+}
+
 std::basic_string<uint8_t> CowWriter::Compress(const void* data, size_t length) {
     switch (compression_) {
         case kCowCompressGz: {
@@ -345,11 +371,23 @@
 }
 
 bool CowWriter::Finalize() {
-    footer_.op.ops_size = ops_.size();
-    uint64_t pos;
+    auto continue_cluster_size = current_cluster_size_;
+    auto continue_data_size = current_data_size_;
+    auto continue_data_pos = next_data_pos_;
+    auto continue_op_pos = next_op_pos_;
+    auto continue_size = ops_.size();
+    bool extra_cluster = false;
 
-    if (!GetDataPos(&pos)) {
-        PLOG(ERROR) << "failed to get file position";
+    // Footer should be at the end of a file, so if there is data after the current block, end it
+    // and start a new cluster.
+    if (cluster_size_ && current_data_size_ > 0) {
+        EmitCluster();
+        extra_cluster = true;
+    }
+
+    footer_.op.ops_size = ops_.size();
+    if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
+        PLOG(ERROR) << "Failed to seek to footer position.";
         return false;
     }
     memset(&footer_.data.ops_checksum, 0, sizeof(uint8_t) * 32);
@@ -364,16 +402,24 @@
         return false;
     }
 
-    // Re-position for any subsequent writes.
-    if (lseek(fd_.get(), pos, SEEK_SET) < 0) {
-        PLOG(ERROR) << "lseek ops failed";
-        return false;
+    // Reposition for additional Writing
+    if (extra_cluster) {
+        current_cluster_size_ = continue_cluster_size;
+        current_data_size_ = continue_data_size;
+        next_data_pos_ = continue_data_pos;
+        next_op_pos_ = continue_op_pos;
+        ops_.resize(continue_size);
     }
+
     return Sync();
 }
 
 uint64_t CowWriter::GetCowSize() {
-    return next_op_pos_ + sizeof(footer_);
+    if (current_data_size_ > 0) {
+        return next_data_pos_ + sizeof(footer_);
+    } else {
+        return next_op_pos_ + sizeof(footer_);
+    }
 }
 
 bool CowWriter::GetDataPos(uint64_t* pos) {
@@ -387,6 +433,10 @@
 }
 
 bool CowWriter::WriteOperation(const CowOperation& op, const void* data, size_t size) {
+    if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek failed for writing operation.";
+        return false;
+    }
     if (!android::base::WriteFully(fd_, reinterpret_cast<const uint8_t*>(&op), sizeof(op))) {
         return false;
     }
@@ -394,16 +444,36 @@
         if (!WriteRawData(data, size)) return false;
     }
     AddOperation(op);
+    // If there isn't room for another op and the cluster end op, end the current cluster
+    if (cluster_size_ && op.type != kCowClusterOp &&
+        cluster_size_ < current_cluster_size_ + 2 * sizeof(op)) {
+        if (!EmitCluster()) return false;
+    }
     return true;
 }
 
 void CowWriter::AddOperation(const CowOperation& op) {
     footer_.op.num_ops++;
-    next_op_pos_ += sizeof(CowOperation) + GetNextOpOffset(op);
+
+    if (op.type == kCowClusterOp) {
+        current_cluster_size_ = 0;
+        current_data_size_ = 0;
+    } else if (header_.cluster_ops) {
+        current_cluster_size_ += sizeof(op);
+        current_data_size_ += op.data_length;
+    }
+
+    next_data_pos_ += op.data_length + GetNextDataOffset(op, header_.cluster_ops);
+    next_op_pos_ += sizeof(CowOperation) + GetNextOpOffset(op, header_.cluster_ops);
     ops_.insert(ops_.size(), reinterpret_cast<const uint8_t*>(&op), sizeof(op));
 }
 
 bool CowWriter::WriteRawData(const void* data, size_t size) {
+    if (lseek(fd_.get(), next_data_pos_, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek failed for writing data.";
+        return false;
+    }
+
     if (!android::base::WriteFully(fd_, data, size)) {
         return false;
     }
@@ -421,7 +491,7 @@
     return true;
 }
 
-bool CowWriter::CommitMerge(int merged_ops) {
+bool CowWriter::CommitMerge(int merged_ops, bool sync) {
     CHECK(merge_in_progress_);
     header_.num_merge_ops += merged_ops;
 
@@ -436,7 +506,11 @@
         return false;
     }
 
-    return Sync();
+    // Sync only for merging of copy operations.
+    if (sync) {
+        return Sync();
+    }
+    return true;
 }
 
 bool CowWriter::Truncate(off_t length) {
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index 8770255..0e90100 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -120,9 +120,5 @@
 #endif
 }
 
-std::string DeviceInfo::GetSnapuserdFirstStagePidVar() const {
-    return kSnapuserdFirstStagePidVar;
-}
-
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
index 1f08860..d8d3d91 100644
--- a/fs_mgr/libsnapshot/device_info.h
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -39,7 +39,6 @@
     bool SetBootControlMergeStatus(MergeStatus status) override;
     bool SetSlotAsUnbootable(unsigned int slot) override;
     bool IsRecovery() const override;
-    std::string GetSnapuserdFirstStagePidVar() const override;
 
   private:
     bool EnsureBootHal();
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 80766ff..797b8ef 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -57,9 +57,15 @@
     // Size of footer struct
     uint16_t footer_size;
 
+    // Size of op struct
+    uint16_t op_size;
+
     // The size of block operations, in bytes.
     uint32_t block_size;
 
+    // The number of ops to cluster together. 0 For no clustering. Cannot be 1.
+    uint32_t cluster_ops;
+
     // Tracks merge operations completed
     uint64_t num_merge_ops;
 } __attribute__((packed));
@@ -113,13 +119,15 @@
     // For copy operations, this is a block location in the source image.
     //
     // For replace operations, this is a byte offset within the COW's data
-    // section (eg, not landing within the header or metadata). It is an
+    // sections (eg, not landing within the header or metadata). It is an
     // absolute position within the image.
     //
     // For zero operations (replace with all zeroes), this is unused and must
     // be zero.
     //
     // For Label operations, this is the value of the applied label.
+    //
+    // For Cluster operations, this is the length of the following data region
     uint64_t source;
 } __attribute__((packed));
 
@@ -129,6 +137,7 @@
 static constexpr uint8_t kCowReplaceOp = 2;
 static constexpr uint8_t kCowZeroOp = 3;
 static constexpr uint8_t kCowLabelOp = 4;
+static constexpr uint8_t kCowClusterOp = 5;
 static constexpr uint8_t kCowFooterOp = -1;
 
 static constexpr uint8_t kCowCompressNone = 0;
@@ -142,7 +151,10 @@
 
 std::ostream& operator<<(std::ostream& os, CowOperation const& arg);
 
-int64_t GetNextOpOffset(const CowOperation& op);
+int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_size);
+int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_size);
+
+bool IsMetadataOp(const CowOperation& op);
 
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index be69225..62b54f9 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -140,6 +140,8 @@
 
     void UpdateMergeProgress(uint64_t merge_ops) { header_.num_merge_ops += merge_ops; }
 
+    void InitializeMerge();
+
   private:
     bool ParseOps(std::optional<uint64_t> label);
 
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index e9320b0..22ddfa6 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -33,6 +33,9 @@
 
     // Maximum number of blocks that can be written.
     std::optional<uint64_t> max_blocks;
+
+    // Number of CowOperations in a cluster. 0 for no clustering. Cannot be 1.
+    uint32_t cluster_ops = 200;
 };
 
 // Interface for writing to a snapuserd COW. All operations are ordered; merges
@@ -98,7 +101,7 @@
     bool InitializeAppend(android::base::borrowed_fd fd, uint64_t label);
 
     void InitializeMerge(android::base::borrowed_fd fd, CowHeader* header);
-    bool CommitMerge(int merged_ops);
+    bool CommitMerge(int merged_ops, bool sync);
 
     bool Finalize() override;
 
@@ -111,6 +114,7 @@
     virtual bool EmitLabel(uint64_t label) override;
 
   private:
+    bool EmitCluster();
     void SetupHeaders();
     bool ParseOptions();
     bool OpenForWrite();
@@ -120,6 +124,7 @@
     bool WriteOperation(const CowOperation& op, const void* data = nullptr, size_t size = 0);
     void AddOperation(const CowOperation& op);
     std::basic_string<uint8_t> Compress(const void* data, size_t length);
+    void InitPos();
 
     bool SetFd(android::base::borrowed_fd fd);
     bool Sync();
@@ -132,6 +137,10 @@
     CowFooter footer_{};
     int compression_ = 0;
     uint64_t next_op_pos_ = 0;
+    uint64_t next_data_pos_ = 0;
+    uint32_t cluster_size_ = 0;
+    uint32_t current_cluster_size_ = 0;
+    uint64_t current_data_size_ = 0;
     bool is_dev_null_ = false;
     bool merge_in_progress_ = false;
     bool is_block_device_ = false;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
index d5c263d..ef9d648 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
@@ -32,7 +32,6 @@
     MOCK_METHOD(bool, SetBootControlMergeStatus, (MergeStatus status), (override));
     MOCK_METHOD(bool, SetSlotAsUnbootable, (unsigned int slot), (override));
     MOCK_METHOD(bool, IsRecovery, (), (const, override));
-    MOCK_METHOD(std::string, GetSnapuserdFirstStagePidVar, (), (const, override));
 };
 
 }  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 351dce7..0a8567f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -107,7 +107,6 @@
         virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
         virtual bool IsRecovery() const = 0;
         virtual bool IsTestDevice() const { return false; }
-        virtual std::string GetSnapuserdFirstStagePidVar() const = 0;
     };
     virtual ~ISnapshotManager() = default;
 
@@ -307,13 +306,17 @@
     // Helper function for second stage init to restorecon on the rollback indicator.
     static std::string GetGlobalRollbackIndicatorPath();
 
-    // Initiate the transition from first-stage to second-stage snapuserd. This
-    // process involves re-creating the dm-user table entries for each device,
-    // so that they connect to the new daemon. Once all new tables have been
-    // activated, we ask the first-stage daemon to cleanly exit.
-    //
-    // The caller must pass a function which starts snapuserd.
-    bool PerformSecondStageTransition();
+    // Detach dm-user devices from the current snapuserd, and populate
+    // |snapuserd_argv| with the necessary arguments to restart snapuserd
+    // and reattach them.
+    bool DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv);
+
+    // Perform the transition from the selinux stage of snapuserd into the
+    // second-stage of snapuserd. This process involves re-creating the dm-user
+    // table entries for each device, so that they connect to the new daemon.
+    // Once all new tables have been activated, we ask the first-stage daemon
+    // to cleanly exit.
+    bool PerformSecondStageInitTransition();
 
     // ISnapshotManager overrides.
     bool BeginUpdate() override;
@@ -345,6 +348,18 @@
     bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override;
     bool UnmapAllSnapshots() override;
 
+    // We can't use WaitForFile during first-stage init, because ueventd is not
+    // running and therefore will not automatically create symlinks. Instead,
+    // we let init provide us with the correct function to use to ensure
+    // uevents have been processed and symlink/mknod calls completed.
+    void SetUeventRegenCallback(std::function<bool(const std::string&)> callback) {
+        uevent_regen_callback_ = callback;
+    }
+
+    // If true, compression is enabled for this update. This is used by
+    // first-stage to decide whether to launch snapuserd.
+    bool IsSnapuserdRequired();
+
   private:
     FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
     FRIEND_TEST(SnapshotTest, CreateSnapshot);
@@ -448,6 +463,10 @@
                       const std::string& base_device, const std::chrono::milliseconds& timeout_ms,
                       std::string* path);
 
+    // Map the source device used for dm-user.
+    bool MapSourceDevice(LockedFile* lock, const std::string& name,
+                         const std::chrono::milliseconds& timeout_ms, std::string* path);
+
     // Map a COW image that was previous created with CreateCowImage.
     std::optional<std::string> MapCowImage(const std::string& name,
                                            const std::chrono::milliseconds& timeout_ms);
@@ -506,11 +525,13 @@
     std::string GetMergeStateFilePath() const;
 
     // Helpers for merging.
+    bool MergeSecondPhaseSnapshots(LockedFile* lock);
     bool SwitchSnapshotToMerge(LockedFile* lock, const std::string& name);
     bool RewriteSnapshotDeviceTable(const std::string& dm_name);
     bool MarkSnapshotMergeCompleted(LockedFile* snapshot_lock, const std::string& snapshot_name);
     void AcknowledgeMergeSuccess(LockedFile* lock);
     void AcknowledgeMergeFailure();
+    MergePhase DecideMergePhase(const SnapshotStatus& status);
     std::unique_ptr<LpMetadata> ReadCurrentMetadata();
 
     enum class MetadataPartitionState {
@@ -543,7 +564,8 @@
     //   UpdateState::MergeNeedsReboot
     UpdateState CheckMergeState(const std::function<bool()>& before_cancel);
     UpdateState CheckMergeState(LockedFile* lock, const std::function<bool()>& before_cancel);
-    UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name);
+    UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name,
+                                      const SnapshotUpdateStatus& update_status);
 
     // Interact with status files under /metadata/ota/snapshots.
     bool WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus& status);
@@ -553,12 +575,9 @@
     std::string GetSnapshotBootIndicatorPath();
     std::string GetRollbackIndicatorPath();
     std::string GetForwardMergeIndicatorPath();
+    std::string GetOldPartitionMetadataPath();
 
-    // Return the name of the device holding the "snapshot" or "snapshot-merge"
-    // target. This may not be the final device presented via MapSnapshot(), if
-    // for example there is a linear segment.
-    std::string GetSnapshotDeviceName(const std::string& snapshot_name,
-                                      const SnapshotStatus& status);
+    const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock);
 
     bool MapAllPartitions(LockedFile* lock, const std::string& super_device, uint32_t slot,
                           const std::chrono::milliseconds& timeout_ms);
@@ -615,6 +634,9 @@
     // The reverse of MapPartitionWithSnapshot.
     bool UnmapPartitionWithSnapshot(LockedFile* lock, const std::string& target_partition_name);
 
+    // Unmap a dm-user device through snapuserd.
+    bool UnmapDmUserDevice(const std::string& snapshot_name);
+
     // If there isn't a previous update, return true. |needs_merge| is set to false.
     // If there is a previous update but the device has not boot into it, tries to cancel the
     //   update and delete any snapshots. Return true if successful. |needs_merge| is set to false.
@@ -655,8 +677,8 @@
 
     // Helper for RemoveAllSnapshots.
     // Check whether |name| should be deleted as a snapshot name.
-    bool ShouldDeleteSnapshot(LockedFile* lock, const std::map<std::string, bool>& flashing_status,
-                              Slot current_slot, const std::string& name);
+    bool ShouldDeleteSnapshot(const std::map<std::string, bool>& flashing_status, Slot current_slot,
+                              const std::string& name);
 
     // Create or delete forward merge indicator given |wipe|. Iff wipe is scheduled,
     // allow forward merge on FDR.
@@ -676,6 +698,25 @@
     // Same as above, but for paths only (no major:minor device strings).
     bool GetMappedImageDevicePath(const std::string& device_name, std::string* device_path);
 
+    // Wait for a device to be created by ueventd (eg, its symlink or node to be populated).
+    // This is needed for any code that uses device-mapper path in first-stage init. If
+    // |timeout_ms| is empty or the given device is not a path, WaitForDevice immediately
+    // returns true.
+    bool WaitForDevice(const std::string& device, std::chrono::milliseconds timeout_ms);
+
+    enum class InitTransition { SELINUX_DETACH, SECOND_STAGE };
+
+    // Initiate the transition from first-stage to second-stage snapuserd. This
+    // process involves re-creating the dm-user table entries for each device,
+    // so that they connect to the new daemon. Once all new tables have been
+    // activated, we ask the first-stage daemon to cleanly exit.
+    //
+    // If the mode is SELINUX_DETACH, snapuserd_argv must be non-null and will
+    // be populated with a list of snapuserd arguments to pass to execve(). It
+    // is otherwise ignored.
+    bool PerformInitTransition(InitTransition transition,
+                               std::vector<std::string>* snapuserd_argv = nullptr);
+
     std::string gsid_dir_;
     std::string metadata_dir_;
     std::unique_ptr<IDeviceInfo> device_;
@@ -683,7 +724,9 @@
     bool has_local_image_manager_ = false;
     bool use_first_stage_snapuserd_ = false;
     bool in_factory_data_reset_ = false;
+    std::function<bool(const std::string&)> uevent_regen_callback_;
     std::unique_ptr<SnapuserdClient> snapuserd_client_;
+    std::unique_ptr<LpMetadata> old_partition_metadata_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
index 0e9ba9e..1dab361 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
@@ -30,18 +30,11 @@
 
 static constexpr uint32_t PACKET_SIZE = 512;
 
-static constexpr char kSnapuserdSocketFirstStage[] = "snapuserd_first_stage";
 static constexpr char kSnapuserdSocket[] = "snapuserd";
 
-static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID";
-
 // Ensure that the second-stage daemon for snapuserd is running.
 bool EnsureSnapuserdStarted();
 
-// Start the first-stage version of snapuserd, returning its pid. This is used
-// by first-stage init, as well as vts_libsnapshot_test. On failure, -1 is returned.
-pid_t StartFirstStageSnapuserd();
-
 class SnapuserdClient {
   private:
     android::base::unique_fd sockfd_;
@@ -75,6 +68,11 @@
     // Wait for snapuserd to disassociate with a dm-user control device. This
     // must ONLY be called if the control device has already been deleted.
     bool WaitForDeviceDelete(const std::string& control_device);
+
+    // Detach snapuserd. This shuts down the listener socket, and will cause
+    // snapuserd to gracefully exit once all handler threads have terminated.
+    // This should only be used on first-stage instances of snapuserd.
+    bool DetachSnapuserd();
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
index e8dbe6e..7941e68 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
@@ -50,6 +50,8 @@
 static constexpr uint32_t BLOCK_SIZE = 4096;
 static constexpr uint32_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SIZE) - 1);
 
+#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d))
+
 // This structure represents the kernel COW header.
 // All the below fields should be in Little Endian format.
 struct disk_header {
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 1d1510a..ff0047e 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -51,8 +51,8 @@
 extern std::unique_ptr<SnapshotManager> sm;
 extern class TestDeviceInfo* test_device;
 extern std::string fake_super;
-static constexpr uint64_t kSuperSize = 16_MiB + 4_KiB;
-static constexpr uint64_t kGroupSize = 16_MiB;
+static constexpr uint64_t kSuperSize = 32_MiB + 4_KiB;
+static constexpr uint64_t kGroupSize = 32_MiB;
 
 // Redirect requests for "super" to our fake super partition.
 class TestPartitionOpener final : public android::fs_mgr::PartitionOpener {
@@ -96,7 +96,6 @@
         return true;
     }
     bool IsTestDevice() const override { return true; }
-    std::string GetSnapuserdFirstStagePidVar() const override { return {}; }
 
     bool IsSlotUnbootable(uint32_t slot) { return unbootable_slots_.count(slot) != 0; }
 
diff --git a/fs_mgr/libsnapshot/inspect_cow.cpp b/fs_mgr/libsnapshot/inspect_cow.cpp
index 6046bad..453b5c6 100644
--- a/fs_mgr/libsnapshot/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/inspect_cow.cpp
@@ -14,6 +14,7 @@
 // limitations under the License.
 //
 #include <stdio.h>
+#include <unistd.h>
 
 #include <iostream>
 #include <string>
@@ -34,7 +35,31 @@
     }
 }
 
-static bool Inspect(const std::string& path) {
+static void usage(void) {
+    LOG(ERROR) << "Usage: inspect_cow [-sd] <COW_FILE>";
+    LOG(ERROR) << "\t -s Run Silent";
+    LOG(ERROR) << "\t -d Attempt to decompress\n";
+}
+
+// Sink that always appends to the end of a string.
+class StringSink : public IByteSink {
+  public:
+    void* GetBuffer(size_t requested, size_t* actual) override {
+        size_t old_size = stream_.size();
+        stream_.resize(old_size + requested, '\0');
+        *actual = requested;
+        return stream_.data() + old_size;
+    }
+    bool ReturnData(void*, size_t) override { return true; }
+    void Reset() { stream_.clear(); }
+
+    std::string& stream() { return stream_; }
+
+  private:
+    std::string stream_;
+};
+
+static bool Inspect(const std::string& path, bool silent, bool decompress) {
     android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
     if (fd < 0) {
         PLOG(ERROR) << "open failed: " << path;
@@ -52,38 +77,73 @@
         LOG(ERROR) << "could not get header: " << path;
         return false;
     }
+    CowFooter footer;
+    bool has_footer = false;
+    if (reader.GetFooter(&footer)) has_footer = true;
 
-    std::cout << "Major version: " << header.major_version << "\n";
-    std::cout << "Minor version: " << header.minor_version << "\n";
-    std::cout << "Header size: " << header.header_size << "\n";
-    std::cout << "Footer size: " << header.footer_size << "\n";
-    std::cout << "Block size: " << header.block_size << "\n";
-    std::cout << "\n";
+    if (!silent) {
+        std::cout << "Major version: " << header.major_version << "\n";
+        std::cout << "Minor version: " << header.minor_version << "\n";
+        std::cout << "Header size: " << header.header_size << "\n";
+        std::cout << "Footer size: " << header.footer_size << "\n";
+        std::cout << "Block size: " << header.block_size << "\n";
+        std::cout << "\n";
+        if (has_footer) {
+            std::cout << "Total Ops size: " << footer.op.ops_size << "\n";
+            std::cout << "Number of Ops: " << footer.op.num_ops << "\n";
+            std::cout << "\n";
+        }
+    }
 
     auto iter = reader.GetOpIter();
+    StringSink sink;
+    bool success = true;
     while (!iter->Done()) {
         const CowOperation& op = iter->Get();
 
-        std::cout << op << "\n";
+        if (!silent) std::cout << op << "\n";
+
+        if (decompress && op.type == kCowReplaceOp && op.compression != kCowCompressNone) {
+            if (!reader.ReadData(op, &sink)) {
+                std::cerr << "Failed to decompress for :" << op << "\n";
+                success = false;
+            }
+            sink.Reset();
+        }
 
         iter->Next();
     }
 
-    return true;
+    return success;
 }
 
 }  // namespace snapshot
 }  // namespace android
 
 int main(int argc, char** argv) {
+    int ch;
+    bool silent = false;
+    bool decompress = false;
+    while ((ch = getopt(argc, argv, "sd")) != -1) {
+        switch (ch) {
+            case 's':
+                silent = true;
+                break;
+            case 'd':
+                decompress = true;
+                break;
+            default:
+                android::snapshot::usage();
+        }
+    }
     android::base::InitLogging(argv, android::snapshot::MyLogger);
 
-    if (argc < 2) {
-        LOG(ERROR) << "Usage: inspect_cow <COW_FILE>";
+    if (argc < optind + 1) {
+        android::snapshot::usage();
         return 1;
     }
 
-    if (!android::snapshot::Inspect(argv[1])) {
+    if (!android::snapshot::Inspect(argv[optind], silent, decompress)) {
         return 1;
     }
     return 0;
diff --git a/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp b/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
index d0b8f52..6a5754d 100644
--- a/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
+++ b/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
@@ -53,6 +53,7 @@
 
 DEFINE_string(source_tf, "", "Source target files (dir or zip file) for incremental payloads");
 DEFINE_string(compression, "gz", "Compression type to use (none or gz)");
+DEFINE_uint32(cluster_ops, 0, "Number of Cow Ops per cluster (0 or >1)");
 
 void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
               unsigned int, const char* message) {
@@ -189,6 +190,7 @@
     CowOptions options;
     options.block_size = kBlockSize;
     options.compression = FLAGS_compression;
+    options.cluster_ops = FLAGS_cluster_ops;
 
     writer_ = std::make_unique<CowWriter>(options);
     if (!writer_->Initialize(std::move(fd))) {
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 6595707..ebda430 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -132,8 +132,8 @@
     return partition_name + "-base";
 }
 
-static std::string GetSnapshotExtraDeviceName(const std::string& snapshot_name) {
-    return snapshot_name + "-inner";
+static std::string GetSourceDeviceName(const std::string& partition_name) {
+    return partition_name + "-src";
 }
 
 bool SnapshotManager::BeginUpdate() {
@@ -157,6 +157,9 @@
         images_->RemoveAllImages();
     }
 
+    // Clear any cached metadata (this allows re-using one manager across tests).
+    old_partition_metadata_ = nullptr;
+
     auto state = ReadUpdateState(file.get());
     if (state != UpdateState::None) {
         LOG(ERROR) << "An update is already in progress, cannot begin a new update";
@@ -255,6 +258,7 @@
             GetSnapshotBootIndicatorPath(),
             GetRollbackIndicatorPath(),
             GetForwardMergeIndicatorPath(),
+            GetOldPartitionMetadataPath(),
     };
     for (const auto& file : files) {
         RemoveFileIfExists(file);
@@ -280,7 +284,7 @@
         return false;
     }
 
-    if (!IsCompressionEnabled() && !EnsureNoOverflowSnapshot(lock.get())) {
+    if (!EnsureNoOverflowSnapshot(lock.get())) {
         LOG(ERROR) << "Cannot ensure there are no overflow snapshots.";
         return false;
     }
@@ -349,6 +353,7 @@
     status->set_state(SnapshotState::CREATED);
     status->set_sectors_allocated(0);
     status->set_metadata_sectors(0);
+    status->set_compression_enabled(IsCompressionEnabled());
 
     if (!WriteSnapshotStatus(lock, *status)) {
         PLOG(ERROR) << "Could not write snapshot status: " << status->name();
@@ -408,10 +413,12 @@
     if (!dm.CreateDevice(name, table, path, timeout_ms)) {
         return false;
     }
+    if (!WaitForDevice(*path, timeout_ms)) {
+        return false;
+    }
 
     auto control_device = "/dev/dm-user/" + misc_name;
-    if (!android::fs_mgr::WaitForFile(control_device, timeout_ms)) {
-        LOG(ERROR) << "Timed out waiting for dm-user misc device: " << control_device;
+    if (!WaitForDevice(control_device, timeout_ms)) {
         return false;
     }
 
@@ -462,8 +469,13 @@
         LOG(ERROR) << "Invalid snapshot size for " << base_device << ": " << status.snapshot_size();
         return false;
     }
+    if (status.device_size() != status.snapshot_size()) {
+        LOG(ERROR) << "Device size and snapshot size must be the same (device size = "
+                   << status.device_size() << ", snapshot size = " << status.snapshot_size();
+        return false;
+    }
+
     uint64_t snapshot_sectors = status.snapshot_size() / kSectorSize;
-    uint64_t linear_sectors = (status.device_size() - status.snapshot_size()) / kSectorSize;
 
     auto& dm = DeviceMapper::Instance();
 
@@ -471,7 +483,8 @@
     // have completed merging, but the start of the merge process is considered
     // atomic.
     SnapshotStorageMode mode;
-    switch (ReadUpdateState(lock)) {
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+    switch (update_status.state()) {
         case UpdateState::MergeCompleted:
         case UpdateState::MergeNeedsReboot:
             LOG(ERROR) << "Should not create a snapshot device for " << name
@@ -481,52 +494,24 @@
         case UpdateState::MergeFailed:
             // Note: MergeFailed indicates that a merge is in progress, but
             // is possibly stalled. We still have to honor the merge.
-            mode = SnapshotStorageMode::Merge;
+            if (DecideMergePhase(status) == update_status.merge_phase()) {
+                mode = SnapshotStorageMode::Merge;
+            } else {
+                mode = SnapshotStorageMode::Persistent;
+            }
             break;
         default:
             mode = SnapshotStorageMode::Persistent;
             break;
     }
 
-    // The kernel (tested on 4.19) crashes horribly if a device has both a snapshot
-    // and a linear target in the same table. Instead, we stack them, and give the
-    // snapshot device a different name. It is not exposed to the caller in this
-    // case.
-    auto snap_name = (linear_sectors > 0) ? GetSnapshotExtraDeviceName(name) : name;
-
     DmTable table;
     table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_device, mode,
                                     kSnapshotChunkSize);
-    if (!dm.CreateDevice(snap_name, table, dev_path, timeout_ms)) {
-        LOG(ERROR) << "Could not create snapshot device: " << snap_name;
+    if (!dm.CreateDevice(name, table, dev_path, timeout_ms)) {
+        LOG(ERROR) << "Could not create snapshot device: " << name;
         return false;
     }
-
-    if (linear_sectors) {
-        std::string snap_dev;
-        if (!dm.GetDeviceString(snap_name, &snap_dev)) {
-            LOG(ERROR) << "Cannot determine major/minor for: " << snap_name;
-            return false;
-        }
-
-        // Our stacking will looks like this:
-        //     [linear, linear] ; to snapshot, and non-snapshot region of base device
-        //     [snapshot-inner]
-        //     [base device]   [cow]
-        DmTable table;
-        table.Emplace<DmTargetLinear>(0, snapshot_sectors, snap_dev, 0);
-        table.Emplace<DmTargetLinear>(snapshot_sectors, linear_sectors, base_device,
-                                      snapshot_sectors);
-        if (!dm.CreateDevice(name, table, dev_path, timeout_ms)) {
-            LOG(ERROR) << "Could not create outer snapshot device: " << name;
-            dm.DeleteDevice(snap_name);
-            return false;
-        }
-    }
-
-    // :TODO: when merging is implemented, we need to add an argument to the
-    // status indicating how much progress is left to merge. (device-mapper
-    // does not retain the initial values, so we can't derive them.)
     return true;
 }
 
@@ -554,6 +539,36 @@
     return std::nullopt;
 }
 
+bool SnapshotManager::MapSourceDevice(LockedFile* lock, const std::string& name,
+                                      const std::chrono::milliseconds& timeout_ms,
+                                      std::string* path) {
+    CHECK(lock);
+
+    auto metadata = ReadOldPartitionMetadata(lock);
+    if (!metadata) {
+        LOG(ERROR) << "Could not map source device due to missing or corrupt metadata";
+        return false;
+    }
+
+    auto old_name = GetOtherPartitionName(name);
+    auto slot_suffix = device_->GetSlotSuffix();
+    auto slot = SlotNumberForSlotSuffix(slot_suffix);
+
+    CreateLogicalPartitionParams params = {
+            .block_device = device_->GetSuperDevice(slot),
+            .metadata = metadata,
+            .partition_name = old_name,
+            .timeout_ms = timeout_ms,
+            .device_name = GetSourceDeviceName(name),
+            .partition_opener = &device_->GetPartitionOpener(),
+    };
+    if (!CreateLogicalPartition(std::move(params), path)) {
+        LOG(ERROR) << "Could not create source device for snapshot " << name;
+        return false;
+    }
+    return true;
+}
+
 bool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {
     CHECK(lock);
 
@@ -562,13 +577,6 @@
         LOG(ERROR) << "Could not delete snapshot device: " << name;
         return false;
     }
-
-    auto snapshot_extra_device = GetSnapshotExtraDeviceName(name);
-    if (!dm.DeleteDeviceIfExists(snapshot_extra_device)) {
-        LOG(ERROR) << "Could not delete snapshot inner device: " << snapshot_extra_device;
-        return false;
-    }
-
     return true;
 }
 
@@ -673,6 +681,10 @@
         }
     }
 
+    bool compression_enabled = false;
+
+    std::vector<std::string> first_merge_group;
+
     uint64_t total_cow_file_size = 0;
     DmTargetSnapshot::Status initial_target_values = {};
     for (const auto& snapshot : snapshots) {
@@ -689,6 +701,11 @@
             return false;
         }
         total_cow_file_size += snapshot_status.cow_file_size();
+
+        compression_enabled |= snapshot_status.compression_enabled();
+        if (DecideMergePhase(snapshot_status) == MergePhase::FIRST_PHASE) {
+            first_merge_group.emplace_back(snapshot);
+        }
     }
 
     if (cow_file_size) {
@@ -700,15 +717,28 @@
     initial_status.set_sectors_allocated(initial_target_values.sectors_allocated);
     initial_status.set_total_sectors(initial_target_values.total_sectors);
     initial_status.set_metadata_sectors(initial_target_values.metadata_sectors);
+    initial_status.set_compression_enabled(compression_enabled);
+
+    // If any partitions shrunk, we need to merge them before we merge any other
+    // partitions (see b/177935716). Otherwise, a merge from another partition
+    // may overwrite the source block of a copy operation.
+    const std::vector<std::string>* merge_group;
+    if (first_merge_group.empty()) {
+        merge_group = &snapshots;
+        initial_status.set_merge_phase(MergePhase::SECOND_PHASE);
+    } else {
+        merge_group = &first_merge_group;
+        initial_status.set_merge_phase(MergePhase::FIRST_PHASE);
+    }
 
     // Point of no return - mark that we're starting a merge. From now on every
-    // snapshot must be a merge target.
+    // eligible snapshot must be a merge target.
     if (!WriteSnapshotUpdateStatus(lock.get(), initial_status)) {
         return false;
     }
 
     bool rewrote_all = true;
-    for (const auto& snapshot : snapshots) {
+    for (const auto& snapshot : *merge_group) {
         // If this fails, we have no choice but to continue. Everything must
         // be merged. This is not an ideal state to be in, but it is safe,
         // because we the next boot will try again.
@@ -741,16 +771,15 @@
 
     // After this, we return true because we technically did switch to a merge
     // target. Everything else we do here is just informational.
-    auto dm_name = GetSnapshotDeviceName(name, status);
-    if (!RewriteSnapshotDeviceTable(dm_name)) {
+    if (!RewriteSnapshotDeviceTable(name)) {
         return false;
     }
 
     status.set_state(SnapshotState::MERGING);
 
     DmTargetSnapshot::Status dm_status;
-    if (!QuerySnapshotStatus(dm_name, nullptr, &dm_status)) {
-        LOG(ERROR) << "Could not query merge status for snapshot: " << dm_name;
+    if (!QuerySnapshotStatus(name, nullptr, &dm_status)) {
+        LOG(ERROR) << "Could not query merge status for snapshot: " << name;
     }
     status.set_sectors_allocated(dm_status.sectors_allocated);
     status.set_metadata_sectors(dm_status.metadata_sectors);
@@ -760,33 +789,33 @@
     return true;
 }
 
-bool SnapshotManager::RewriteSnapshotDeviceTable(const std::string& dm_name) {
+bool SnapshotManager::RewriteSnapshotDeviceTable(const std::string& name) {
     auto& dm = DeviceMapper::Instance();
 
     std::vector<DeviceMapper::TargetInfo> old_targets;
-    if (!dm.GetTableInfo(dm_name, &old_targets)) {
-        LOG(ERROR) << "Could not read snapshot device table: " << dm_name;
+    if (!dm.GetTableInfo(name, &old_targets)) {
+        LOG(ERROR) << "Could not read snapshot device table: " << name;
         return false;
     }
     if (old_targets.size() != 1 || DeviceMapper::GetTargetType(old_targets[0].spec) != "snapshot") {
-        LOG(ERROR) << "Unexpected device-mapper table for snapshot: " << dm_name;
+        LOG(ERROR) << "Unexpected device-mapper table for snapshot: " << name;
         return false;
     }
 
     std::string base_device, cow_device;
     if (!DmTargetSnapshot::GetDevicesFromParams(old_targets[0].data, &base_device, &cow_device)) {
-        LOG(ERROR) << "Could not derive underlying devices for snapshot: " << dm_name;
+        LOG(ERROR) << "Could not derive underlying devices for snapshot: " << name;
         return false;
     }
 
     DmTable table;
     table.Emplace<DmTargetSnapshot>(0, old_targets[0].spec.length, base_device, cow_device,
                                     SnapshotStorageMode::Merge, kSnapshotChunkSize);
-    if (!dm.LoadTableAndActivate(dm_name, table)) {
-        LOG(ERROR) << "Could not swap device-mapper tables on snapshot device " << dm_name;
+    if (!dm.LoadTableAndActivate(name, table)) {
+        LOG(ERROR) << "Could not swap device-mapper tables on snapshot device " << name;
         return false;
     }
-    LOG(INFO) << "Successfully switched snapshot device to a merge target: " << dm_name;
+    LOG(INFO) << "Successfully switched snapshot device to a merge target: " << name;
     return true;
 }
 
@@ -900,13 +929,13 @@
 
 UpdateState SnapshotManager::CheckMergeState(LockedFile* lock,
                                              const std::function<bool()>& before_cancel) {
-    UpdateState state = ReadUpdateState(lock);
-    switch (state) {
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+    switch (update_status.state()) {
         case UpdateState::None:
         case UpdateState::MergeCompleted:
             // Harmless races are allowed between two callers of WaitForMerge,
             // so in both of these cases we just propagate the state.
-            return state;
+            return update_status.state();
 
         case UpdateState::Merging:
         case UpdateState::MergeNeedsReboot:
@@ -923,10 +952,10 @@
             if (HandleCancelledUpdate(lock, before_cancel)) {
                 return UpdateState::Cancelled;
             }
-            return state;
+            return update_status.state();
 
         default:
-            return state;
+            return update_status.state();
     }
 
     std::vector<std::string> snapshots;
@@ -938,8 +967,9 @@
     bool failed = false;
     bool merging = false;
     bool needs_reboot = false;
+    bool wrong_phase = false;
     for (const auto& snapshot : snapshots) {
-        UpdateState snapshot_state = CheckTargetMergeState(lock, snapshot);
+        UpdateState snapshot_state = CheckTargetMergeState(lock, snapshot, update_status);
         switch (snapshot_state) {
             case UpdateState::MergeFailed:
                 failed = true;
@@ -955,6 +985,9 @@
             case UpdateState::Cancelled:
                 cancelled = true;
                 break;
+            case UpdateState::None:
+                wrong_phase = true;
+                break;
             default:
                 LOG(ERROR) << "Unknown merge status for \"" << snapshot << "\": "
                            << "\"" << snapshot_state << "\"";
@@ -974,6 +1007,14 @@
         // it in WaitForMerge rather than here and elsewhere.
         return UpdateState::MergeFailed;
     }
+    if (wrong_phase) {
+        // If we got here, no other partitions are being merged, and nothing
+        // failed to merge. It's safe to move to the next merge phase.
+        if (!MergeSecondPhaseSnapshots(lock)) {
+            return UpdateState::MergeFailed;
+        }
+        return UpdateState::Merging;
+    }
     if (needs_reboot) {
         WriteUpdateState(lock, UpdateState::MergeNeedsReboot);
         return UpdateState::MergeNeedsReboot;
@@ -989,17 +1030,16 @@
     return UpdateState::MergeCompleted;
 }
 
-UpdateState SnapshotManager::CheckTargetMergeState(LockedFile* lock, const std::string& name) {
+UpdateState SnapshotManager::CheckTargetMergeState(LockedFile* lock, const std::string& name,
+                                                   const SnapshotUpdateStatus& update_status) {
     SnapshotStatus snapshot_status;
     if (!ReadSnapshotStatus(lock, name, &snapshot_status)) {
         return UpdateState::MergeFailed;
     }
 
-    std::string dm_name = GetSnapshotDeviceName(name, snapshot_status);
-
     std::unique_ptr<LpMetadata> current_metadata;
 
-    if (!IsSnapshotDevice(dm_name)) {
+    if (!IsSnapshotDevice(name)) {
         if (!current_metadata) {
             current_metadata = ReadCurrentMetadata();
         }
@@ -1013,7 +1053,7 @@
         // During a check, we decided the merge was complete, but we were unable to
         // collapse the device-mapper stack and perform COW cleanup. If we haven't
         // rebooted after this check, the device will still be a snapshot-merge
-        // target. If the have rebooted, the device will now be a linear target,
+        // target. If we have rebooted, the device will now be a linear target,
         // and we can try cleanup again.
         if (snapshot_status.state() == SnapshotState::MERGE_COMPLETED) {
             // NB: It's okay if this fails now, we gave cleanup our best effort.
@@ -1021,7 +1061,7 @@
             return UpdateState::MergeCompleted;
         }
 
-        LOG(ERROR) << "Expected snapshot or snapshot-merge for device: " << dm_name;
+        LOG(ERROR) << "Expected snapshot or snapshot-merge for device: " << name;
         return UpdateState::MergeFailed;
     }
 
@@ -1031,9 +1071,15 @@
 
     std::string target_type;
     DmTargetSnapshot::Status status;
-    if (!QuerySnapshotStatus(dm_name, &target_type, &status)) {
+    if (!QuerySnapshotStatus(name, &target_type, &status)) {
         return UpdateState::MergeFailed;
     }
+    if (target_type == "snapshot" &&
+        DecideMergePhase(snapshot_status) == MergePhase::SECOND_PHASE &&
+        update_status.merge_phase() == MergePhase::FIRST_PHASE) {
+        // The snapshot is not being merged because it's in the wrong phase.
+        return UpdateState::None;
+    }
     if (target_type != "snapshot-merge") {
         // We can get here if we failed to rewrite the target type in
         // InitiateMerge(). If we failed to create the target in first-stage
@@ -1069,6 +1115,38 @@
     return UpdateState::MergeCompleted;
 }
 
+bool SnapshotManager::MergeSecondPhaseSnapshots(LockedFile* lock) {
+    std::vector<std::string> snapshots;
+    if (!ListSnapshots(lock, &snapshots)) {
+        return UpdateState::MergeFailed;
+    }
+
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+    CHECK(update_status.state() == UpdateState::Merging);
+    CHECK(update_status.merge_phase() == MergePhase::FIRST_PHASE);
+
+    update_status.set_merge_phase(MergePhase::SECOND_PHASE);
+    if (!WriteSnapshotUpdateStatus(lock, update_status)) {
+        return false;
+    }
+
+    bool rewrote_all = true;
+    for (const auto& snapshot : snapshots) {
+        SnapshotStatus snapshot_status;
+        if (!ReadSnapshotStatus(lock, snapshot, &snapshot_status)) {
+            return UpdateState::MergeFailed;
+        }
+        if (DecideMergePhase(snapshot_status) != MergePhase::SECOND_PHASE) {
+            continue;
+        }
+        if (!SwitchSnapshotToMerge(lock, snapshot)) {
+            LOG(ERROR) << "Failed to switch snapshot to a second-phase merge target: " << snapshot;
+            rewrote_all = false;
+        }
+    }
+    return rewrote_all;
+}
+
 std::string SnapshotManager::GetSnapshotBootIndicatorPath() {
     return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath);
 }
@@ -1081,6 +1159,10 @@
     return metadata_dir_ + "/allow-forward-merge";
 }
 
+std::string SnapshotManager::GetOldPartitionMetadataPath() {
+    return metadata_dir_ + "/old-partition-metadata";
+}
+
 void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
     // It's not possible to remove update state in recovery, so write an
     // indicator that cleanup is needed on reboot. If a factory data reset
@@ -1116,21 +1198,20 @@
 
 bool SnapshotManager::OnSnapshotMergeComplete(LockedFile* lock, const std::string& name,
                                               const SnapshotStatus& status) {
-    auto dm_name = GetSnapshotDeviceName(name, status);
-    if (IsSnapshotDevice(dm_name)) {
+    if (IsSnapshotDevice(name)) {
         // We are extra-cautious here, to avoid deleting the wrong table.
         std::string target_type;
         DmTargetSnapshot::Status dm_status;
-        if (!QuerySnapshotStatus(dm_name, &target_type, &dm_status)) {
+        if (!QuerySnapshotStatus(name, &target_type, &dm_status)) {
             return false;
         }
         if (target_type != "snapshot-merge") {
             LOG(ERROR) << "Unexpected target type " << target_type
-                       << " for snapshot device: " << dm_name;
+                       << " for snapshot device: " << name;
             return false;
         }
         if (dm_status.sectors_allocated != dm_status.metadata_sectors) {
-            LOG(ERROR) << "Merge is unexpectedly incomplete for device " << dm_name;
+            LOG(ERROR) << "Merge is unexpectedly incomplete for device " << name;
             return false;
         }
         if (!CollapseSnapshotDevice(name, status)) {
@@ -1151,23 +1232,21 @@
 bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
                                              const SnapshotStatus& status) {
     auto& dm = DeviceMapper::Instance();
-    auto dm_name = GetSnapshotDeviceName(name, status);
 
     // Verify we have a snapshot-merge device.
     DeviceMapper::TargetInfo target;
-    if (!GetSingleTarget(dm_name, TableQuery::Table, &target)) {
+    if (!GetSingleTarget(name, TableQuery::Table, &target)) {
         return false;
     }
     if (DeviceMapper::GetTargetType(target.spec) != "snapshot-merge") {
         // This should be impossible, it was checked earlier.
-        LOG(ERROR) << "Snapshot device has invalid target type: " << dm_name;
+        LOG(ERROR) << "Snapshot device has invalid target type: " << name;
         return false;
     }
 
     std::string base_device, cow_device;
     if (!DmTargetSnapshot::GetDevicesFromParams(target.data, &base_device, &cow_device)) {
-        LOG(ERROR) << "Could not parse snapshot device " << dm_name
-                   << " parameters: " << target.data;
+        LOG(ERROR) << "Could not parse snapshot device " << name << " parameters: " << target.data;
         return false;
     }
 
@@ -1178,42 +1257,6 @@
         return false;
     }
 
-    if (dm_name != name) {
-        // We've derived the base device, but we actually need to replace the
-        // table of the outermost device. Do a quick verification that this
-        // device looks like we expect it to.
-        std::vector<DeviceMapper::TargetInfo> outer_table;
-        if (!dm.GetTableInfo(name, &outer_table)) {
-            LOG(ERROR) << "Could not validate outer snapshot table: " << name;
-            return false;
-        }
-        if (outer_table.size() != 2) {
-            LOG(ERROR) << "Expected 2 dm-linear targets for table " << name
-                       << ", got: " << outer_table.size();
-            return false;
-        }
-        for (const auto& target : outer_table) {
-            auto target_type = DeviceMapper::GetTargetType(target.spec);
-            if (target_type != "linear") {
-                LOG(ERROR) << "Outer snapshot table may only contain linear targets, but " << name
-                           << " has target: " << target_type;
-                return false;
-            }
-        }
-        if (outer_table[0].spec.length != snapshot_sectors) {
-            LOG(ERROR) << "dm-snapshot " << name << " should have " << snapshot_sectors
-                       << " sectors, got: " << outer_table[0].spec.length;
-            return false;
-        }
-        uint64_t expected_device_sectors = status.device_size() / kSectorSize;
-        uint64_t actual_device_sectors = outer_table[0].spec.length + outer_table[1].spec.length;
-        if (expected_device_sectors != actual_device_sectors) {
-            LOG(ERROR) << "Outer device " << name << " should have " << expected_device_sectors
-                       << " sectors, got: " << actual_device_sectors;
-            return false;
-        }
-    }
-
     uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
     // Create a DmTable that is identical to the base device.
     CreateLogicalPartitionParams base_device_params{
@@ -1228,7 +1271,6 @@
         return false;
     }
 
-    // Note: we are replacing the *outer* table here, so we do not use dm_name.
     if (!dm.LoadTableAndActivate(name, table)) {
         return false;
     }
@@ -1238,18 +1280,17 @@
     // flushed remaining I/O. We could in theory replace with dm-zero (or
     // re-use the table above), but for now it's better to know why this
     // would fail.
-    if (dm_name != name && !dm.DeleteDeviceIfExists(dm_name)) {
-        LOG(ERROR) << "Unable to delete snapshot device " << dm_name << ", COW cannot be "
-                   << "reclaimed until after reboot.";
-        return false;
+    if (status.compression_enabled()) {
+        UnmapDmUserDevice(name);
     }
-
-    // Cleanup the base device as well, since it is no longer used. This does
-    // not block cleanup.
     auto base_name = GetBaseDeviceName(name);
     if (!dm.DeleteDeviceIfExists(base_name)) {
         LOG(ERROR) << "Unable to delete base device for snapshot: " << base_name;
     }
+    auto source_name = GetSourceDeviceName(name);
+    if (!dm.DeleteDeviceIfExists(source_name)) {
+        LOG(ERROR) << "Unable to delete source device for snapshot: " << source_name;
+    }
     return true;
 }
 
@@ -1288,12 +1329,13 @@
     return RemoveAllUpdateState(lock, before_cancel);
 }
 
-bool SnapshotManager::PerformSecondStageTransition() {
-    LOG(INFO) << "Performing second-stage transition for snapuserd.";
+bool SnapshotManager::PerformInitTransition(InitTransition transition,
+                                            std::vector<std::string>* snapuserd_argv) {
+    LOG(INFO) << "Performing transition for snapuserd.";
 
     // Don't use EnsuerSnapuserdConnected() because this is called from init,
     // and attempting to do so will deadlock.
-    if (!snapuserd_client_) {
+    if (!snapuserd_client_ && transition != InitTransition::SELINUX_DETACH) {
         snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
         if (!snapuserd_client_) {
             LOG(ERROR) << "Unable to connect to snapuserd";
@@ -1339,16 +1381,21 @@
             continue;
         }
 
+        auto misc_name = user_cow_name;
+        if (transition == InitTransition::SELINUX_DETACH) {
+            misc_name += "-selinux";
+        }
+
         DmTable table;
-        table.Emplace<DmTargetUser>(0, target.spec.length, user_cow_name);
+        table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
         if (!dm.LoadTableAndActivate(user_cow_name, table)) {
-            LOG(ERROR) << "Unable to swap tables for " << user_cow_name;
+            LOG(ERROR) << "Unable to swap tables for " << misc_name;
             continue;
         }
 
-        std::string backing_device;
-        if (!dm.GetDmDevicePathByName(GetBaseDeviceName(snapshot), &backing_device)) {
-            LOG(ERROR) << "Could not get device path for " << GetBaseDeviceName(snapshot);
+        std::string source_device;
+        if (!dm.GetDmDevicePathByName(GetSourceDeviceName(snapshot), &source_device)) {
+            LOG(ERROR) << "Could not get device path for " << GetSourceDeviceName(snapshot);
             continue;
         }
 
@@ -1368,23 +1415,33 @@
         }
 
         // Wait for ueventd to acknowledge and create the control device node.
-        std::string control_device = "/dev/dm-user/" + user_cow_name;
-        if (!android::fs_mgr::WaitForFile(control_device, 10s)) {
-            LOG(ERROR) << "Could not find control device: " << control_device;
+        std::string control_device = "/dev/dm-user/" + misc_name;
+        if (!WaitForDevice(control_device, 10s)) {
+            continue;
+        }
+
+        if (transition == InitTransition::SELINUX_DETACH) {
+            auto message = misc_name + "," + cow_image_device + "," + source_device;
+            snapuserd_argv->emplace_back(std::move(message));
+
+            // Do not attempt to connect to the new snapuserd yet, it hasn't
+            // been started. We do however want to wait for the misc device
+            // to have been created.
+            ok_cows++;
             continue;
         }
 
         uint64_t base_sectors =
-                snapuserd_client_->InitDmUserCow(user_cow_name, cow_image_device, backing_device);
+                snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, source_device);
         if (base_sectors == 0) {
             // Unrecoverable as metadata reads from cow device failed
             LOG(FATAL) << "Failed to retrieve base_sectors from Snapuserd";
             return false;
         }
 
-        CHECK(base_sectors == target.spec.length);
+        CHECK(base_sectors <= target.spec.length);
 
-        if (!snapuserd_client_->AttachDmUser(user_cow_name)) {
+        if (!snapuserd_client_->AttachDmUser(misc_name)) {
             // This error is unrecoverable. We cannot proceed because reads to
             // the underlying device will fail.
             LOG(FATAL) << "Could not initialize snapuserd for " << user_cow_name;
@@ -1398,24 +1455,6 @@
         LOG(ERROR) << "Could not transition all snapuserd consumers.";
         return false;
     }
-
-    std::string pid_var = device_->GetSnapuserdFirstStagePidVar();
-    if (pid_var.empty()) {
-        return true;
-    }
-
-    int pid;
-    const char* pid_str = getenv(pid_var.c_str());
-    if (pid_str && android::base::ParseInt(pid_str, &pid)) {
-        if (kill(pid, SIGTERM) < 0 && errno != ESRCH) {
-            LOG(ERROR) << "kill snapuserd failed";
-            return false;
-        }
-    } else {
-        LOG(ERROR) << "Could not find or parse " << kSnapuserdFirstStagePidVar
-                   << " for snapuserd pid";
-        return false;
-    }
     return true;
 }
 
@@ -1538,7 +1577,7 @@
         //    Otherwise (UPDATED flag), only delete snapshots if they are not mapped
         //    as dm-snapshot (for example, after merge completes).
         bool should_unmap = current_slot != Slot::Target;
-        bool should_delete = ShouldDeleteSnapshot(lock, flashing_status, current_slot, name);
+        bool should_delete = ShouldDeleteSnapshot(flashing_status, current_slot, name);
 
         bool partition_ok = true;
         if (should_unmap && !UnmapPartitionWithSnapshot(lock, name)) {
@@ -1572,8 +1611,7 @@
 }
 
 // See comments in RemoveAllSnapshots().
-bool SnapshotManager::ShouldDeleteSnapshot(LockedFile* lock,
-                                           const std::map<std::string, bool>& flashing_status,
+bool SnapshotManager::ShouldDeleteSnapshot(const std::map<std::string, bool>& flashing_status,
                                            Slot current_slot, const std::string& name) {
     if (current_slot != Slot::Target) {
         return true;
@@ -1587,16 +1625,7 @@
         // partition flashed, okay to delete obsolete snapshots
         return true;
     }
-    // partition updated, only delete if not dm-snapshot
-    SnapshotStatus status;
-    if (!ReadSnapshotStatus(lock, name, &status)) {
-        LOG(WARNING) << "Unable to read snapshot status for " << name
-                     << ", guessing snapshot device name";
-        auto extra_name = GetSnapshotExtraDeviceName(name);
-        return !IsSnapshotDevice(name) && !IsSnapshotDevice(extra_name);
-    }
-    auto dm_name = GetSnapshotDeviceName(name, status);
-    return !IsSnapshotDevice(dm_name);
+    return !IsSnapshotDevice(name);
 }
 
 UpdateState SnapshotManager::GetUpdateState(double* progress) {
@@ -1760,15 +1789,6 @@
         }
     }
 
-    if (use_first_stage_snapuserd_) {
-        // Remove the first-stage socket as a precaution, there is no need to
-        // access the daemon anymore and we'll be killing it once second-stage
-        // is running.
-        auto socket = ANDROID_SOCKET_DIR + "/"s + kSnapuserdSocketFirstStage;
-        snapuserd_client_ = nullptr;
-        unlink(socket.c_str());
-    }
-
     LOG(INFO) << "Created logical partitions with snapshot.";
     return true;
 }
@@ -1914,24 +1934,42 @@
     remaining_time = GetRemainingTime(params.timeout_ms, begin);
     if (remaining_time.count() < 0) return false;
 
-    if (context == SnapshotContext::Update && IsCompressionEnabled()) {
+    if (context == SnapshotContext::Update && live_snapshot_status->compression_enabled()) {
         // Stop here, we can't run dm-user yet, the COW isn't built.
         created_devices.Release();
         return true;
     }
 
-    if (IsCompressionEnabled()) {
-        auto name = GetDmUserCowName(params.GetPartitionName());
+    if (live_snapshot_status->compression_enabled()) {
+        // Get the source device (eg the view of the partition from before it was resized).
+        std::string source_device_path;
+        if (!MapSourceDevice(lock, params.GetPartitionName(), remaining_time,
+                             &source_device_path)) {
+            LOG(ERROR) << "Could not map source device for: " << cow_name;
+            return false;
+        }
 
-        // :TODO: need to force init to process uevents for these in first-stage.
+        auto source_device = GetSourceDeviceName(params.GetPartitionName());
+        created_devices.EmplaceBack<AutoUnmapDevice>(&dm, source_device);
+
+        if (!WaitForDevice(source_device_path, remaining_time)) {
+            return false;
+        }
+
         std::string cow_path;
         if (!GetMappedImageDevicePath(cow_name, &cow_path)) {
             LOG(ERROR) << "Could not determine path for: " << cow_name;
             return false;
         }
+        if (!WaitForDevice(cow_path, remaining_time)) {
+            return false;
+        }
+
+        auto name = GetDmUserCowName(params.GetPartitionName());
 
         std::string new_cow_device;
-        if (!MapDmUserCow(lock, name, cow_path, base_path, remaining_time, &new_cow_device)) {
+        if (!MapDmUserCow(lock, name, cow_path, source_device_path, remaining_time,
+                          &new_cow_device)) {
             LOG(ERROR) << "Could not map dm-user device for partition "
                        << params.GetPartitionName();
             return false;
@@ -1975,12 +2013,18 @@
     }
 
     auto& dm = DeviceMapper::Instance();
-    std::string base_name = GetBaseDeviceName(target_partition_name);
+    auto base_name = GetBaseDeviceName(target_partition_name);
     if (!dm.DeleteDeviceIfExists(base_name)) {
         LOG(ERROR) << "Cannot delete base device: " << base_name;
         return false;
     }
 
+    auto source_name = GetSourceDeviceName(target_partition_name);
+    if (!dm.DeleteDeviceIfExists(source_name)) {
+        LOG(ERROR) << "Cannot delete source device: " << source_name;
+        return false;
+    }
+
     LOG(INFO) << "Successfully unmapped snapshot " << target_partition_name;
 
     return true;
@@ -2064,27 +2108,8 @@
 
     auto& dm = DeviceMapper::Instance();
 
-    auto dm_user_name = GetDmUserCowName(name);
-    if (IsCompressionEnabled() && dm.GetState(dm_user_name) != DmDeviceState::INVALID) {
-        if (!EnsureSnapuserdConnected()) {
-            return false;
-        }
-        if (!dm.DeleteDevice(dm_user_name)) {
-            LOG(ERROR) << "Cannot unmap " << dm_user_name;
-            return false;
-        }
-
-        if (!snapuserd_client_->WaitForDeviceDelete(dm_user_name)) {
-            LOG(ERROR) << "Failed to wait for " << dm_user_name << " control device to delete";
-            return false;
-        }
-
-        // Ensure the control device is gone so we don't run into ABA problems.
-        auto control_device = "/dev/dm-user/" + dm_user_name;
-        if (!android::fs_mgr::WaitForFileDeleted(control_device, 10s)) {
-            LOG(ERROR) << "Timed out waiting for " << control_device << " to unlink";
-            return false;
-        }
+    if (IsCompressionEnabled() && !UnmapDmUserDevice(name)) {
+        return false;
     }
 
     auto cow_name = GetCowName(name);
@@ -2101,6 +2126,37 @@
     return true;
 }
 
+bool SnapshotManager::UnmapDmUserDevice(const std::string& snapshot_name) {
+    auto& dm = DeviceMapper::Instance();
+
+    if (!EnsureSnapuserdConnected()) {
+        return false;
+    }
+
+    auto dm_user_name = GetDmUserCowName(snapshot_name);
+    if (dm.GetState(dm_user_name) == DmDeviceState::INVALID) {
+        return true;
+    }
+
+    if (!dm.DeleteDeviceIfExists(dm_user_name)) {
+        LOG(ERROR) << "Cannot unmap " << dm_user_name;
+        return false;
+    }
+
+    if (!snapuserd_client_->WaitForDeviceDelete(dm_user_name)) {
+        LOG(ERROR) << "Failed to wait for " << dm_user_name << " control device to delete";
+        return false;
+    }
+
+    // Ensure the control device is gone so we don't run into ABA problems.
+    auto control_device = "/dev/dm-user/" + dm_user_name;
+    if (!android::fs_mgr::WaitForFileDeleted(control_device, 10s)) {
+        LOG(ERROR) << "Timed out waiting for " << control_device << " to unlink";
+        return false;
+    }
+    return true;
+}
+
 bool SnapshotManager::MapAllSnapshots(const std::chrono::milliseconds& timeout_ms) {
     auto lock = LockExclusive();
     if (!lock) return false;
@@ -2270,8 +2326,15 @@
 }
 
 bool SnapshotManager::WriteUpdateState(LockedFile* lock, UpdateState state) {
-    SnapshotUpdateStatus status = {};
+    SnapshotUpdateStatus status;
     status.set_state(state);
+
+    // If we're transitioning between two valid states (eg, we're not beginning
+    // or ending an OTA), then make sure to propagate the compression bit.
+    if (!(state == UpdateState::Initiated || state == UpdateState::None)) {
+        SnapshotUpdateStatus old_status = ReadSnapshotUpdateStatus(lock);
+        status.set_compression_enabled(old_status.compression_enabled());
+    }
     return WriteSnapshotUpdateStatus(lock, status);
 }
 
@@ -2382,14 +2445,6 @@
     return true;
 }
 
-std::string SnapshotManager::GetSnapshotDeviceName(const std::string& snapshot_name,
-                                                   const SnapshotStatus& status) {
-    if (status.device_size() != status.snapshot_size()) {
-        return GetSnapshotExtraDeviceName(snapshot_name);
-    }
-    return snapshot_name;
-}
-
 bool SnapshotManager::EnsureImageManager() {
     if (images_) return true;
 
@@ -2407,28 +2462,11 @@
         return true;
     }
 
-    std::string socket;
-    if (use_first_stage_snapuserd_) {
-        auto pid = StartFirstStageSnapuserd();
-        if (pid < 0) {
-            LOG(ERROR) << "Failed to start snapuserd";
-            return false;
-        }
-
-        auto pid_str = std::to_string(static_cast<int>(pid));
-        if (setenv(kSnapuserdFirstStagePidVar, pid_str.c_str(), 1) < 0) {
-            PLOG(ERROR) << "setenv failed storing the snapuserd pid";
-        }
-
-        socket = kSnapuserdSocketFirstStage;
-    } else {
-        if (!EnsureSnapuserdStarted()) {
-            return false;
-        }
-        socket = kSnapuserdSocket;
+    if (!use_first_stage_snapuserd_ && !EnsureSnapuserdStarted()) {
+        return false;
     }
 
-    snapuserd_client_ = SnapuserdClient::Connect(socket, 10s);
+    snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
     if (!snapuserd_client_) {
         LOG(ERROR) << "Unable to connect to snapuserd";
         return false;
@@ -2478,6 +2516,12 @@
     auto lock = LockExclusive();
     if (!lock) return Return::Error();
 
+    auto update_state = ReadUpdateState(lock.get());
+    if (update_state != UpdateState::Initiated) {
+        LOG(ERROR) << "Cannot create update snapshots in state " << update_state;
+        return Return::Error();
+    }
+
     // TODO(b/134949511): remove this check. Right now, with overlayfs mounted, the scratch
     // partition takes up a big chunk of space in super, causing COW images to be created on
     // retrofit Virtual A/B devices.
@@ -2572,6 +2616,32 @@
         return Return::Error();
     }
 
+    // If compression is enabled, we need to retain a copy of the old metadata
+    // so we can access original blocks in case they are moved around. We do
+    // not want to rely on the old super metadata slot because we don't
+    // guarantee its validity after the slot switch is successful.
+    if (cow_creator.compression_enabled) {
+        auto metadata = current_metadata->Export();
+        if (!metadata) {
+            LOG(ERROR) << "Could not export current metadata";
+            return Return::Error();
+        }
+
+        auto path = GetOldPartitionMetadataPath();
+        if (!android::fs_mgr::WriteToImageFile(path, *metadata.get())) {
+            LOG(ERROR) << "Cannot write old metadata to " << path;
+            return Return::Error();
+        }
+    }
+
+    SnapshotUpdateStatus status = {};
+    status.set_state(update_state);
+    status.set_compression_enabled(cow_creator.compression_enabled);
+    if (!WriteSnapshotUpdateStatus(lock.get(), status)) {
+        LOG(ERROR) << "Unable to write new update state";
+        return Return::Error();
+    }
+
     created_devices.Release();
     LOG(INFO) << "Successfully created all snapshots for target slot " << target_suffix;
 
@@ -2664,6 +2734,15 @@
             continue;
         }
 
+        // Find the original partition size.
+        auto name = target_partition->name();
+        auto old_partition_name =
+                name.substr(0, name.size() - target_suffix.size()) + cow_creator->current_suffix;
+        auto old_partition = cow_creator->current_metadata->FindPartition(old_partition_name);
+        if (old_partition) {
+            cow_creator_ret->snapshot_status.set_old_partition_size(old_partition->size());
+        }
+
         // Store these device sizes to snapshot status file.
         if (!CreateSnapshot(lock, &cow_creator_ret->snapshot_status)) {
             return Return::Error();
@@ -2750,7 +2829,7 @@
             return Return::Error();
         }
 
-        if (IsCompressionEnabled()) {
+        if (it->second.compression_enabled()) {
             unique_fd fd(open(cow_path.c_str(), O_RDWR | O_CLOEXEC));
             if (fd < 0) {
                 PLOG(ERROR) << "open " << cow_path << " failed for snapshot "
@@ -2844,7 +2923,7 @@
         return nullptr;
     }
 
-    if (IsCompressionEnabled()) {
+    if (status.compression_enabled()) {
         return OpenCompressedSnapshotWriter(lock.get(), source_device, params.GetPartitionName(),
                                             status, paths);
     }
@@ -3169,6 +3248,14 @@
 
     auto& dm = DeviceMapper::Instance();
     for (const auto& snapshot : snapshots) {
+        SnapshotStatus status;
+        if (!ReadSnapshotStatus(lock, snapshot, &status)) {
+            return false;
+        }
+        if (status.compression_enabled()) {
+            continue;
+        }
+
         std::vector<DeviceMapper::TargetInfo> targets;
         if (!dm.GetTableStatus(snapshot, &targets)) {
             LOG(ERROR) << "Could not read snapshot device table: " << snapshot;
@@ -3293,5 +3380,76 @@
     return true;
 }
 
+bool SnapshotManager::WaitForDevice(const std::string& device,
+                                    std::chrono::milliseconds timeout_ms) {
+    if (!android::base::StartsWith(device, "/")) {
+        return true;
+    }
+
+    // In first-stage init, we rely on init setting a callback which can
+    // regenerate uevents and populate /dev for us.
+    if (uevent_regen_callback_) {
+        if (!uevent_regen_callback_(device)) {
+            LOG(ERROR) << "Failed to find device after regenerating uevents: " << device;
+            return false;
+        }
+        return true;
+    }
+
+    // Otherwise, the only kind of device we need to wait for is a dm-user
+    // misc device. Normal calls to DeviceMapper::CreateDevice() guarantee
+    // the path has been created.
+    if (!android::base::StartsWith(device, "/dev/dm-user/")) {
+        return true;
+    }
+
+    if (timeout_ms.count() == 0) {
+        LOG(ERROR) << "No timeout was specified to wait for device: " << device;
+        return false;
+    }
+    if (!android::fs_mgr::WaitForFile(device, timeout_ms)) {
+        LOG(ERROR) << "Timed out waiting for device to appear: " << device;
+        return false;
+    }
+    return true;
+}
+
+bool SnapshotManager::IsSnapuserdRequired() {
+    auto lock = LockExclusive();
+    if (!lock) return false;
+
+    auto status = ReadSnapshotUpdateStatus(lock.get());
+    return status.state() != UpdateState::None && status.compression_enabled();
+}
+
+bool SnapshotManager::DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv) {
+    return PerformInitTransition(InitTransition::SELINUX_DETACH, snapuserd_argv);
+}
+
+bool SnapshotManager::PerformSecondStageInitTransition() {
+    return PerformInitTransition(InitTransition::SECOND_STAGE);
+}
+
+const LpMetadata* SnapshotManager::ReadOldPartitionMetadata(LockedFile* lock) {
+    CHECK(lock);
+
+    if (!old_partition_metadata_) {
+        auto path = GetOldPartitionMetadataPath();
+        old_partition_metadata_ = android::fs_mgr::ReadFromImageFile(path);
+        if (!old_partition_metadata_) {
+            LOG(ERROR) << "Could not read old partition metadata from " << path;
+            return nullptr;
+        }
+    }
+    return old_partition_metadata_.get();
+}
+
+MergePhase SnapshotManager::DecideMergePhase(const SnapshotStatus& status) {
+    if (status.compression_enabled() && status.device_size() < status.old_partition_size()) {
+        return MergePhase::FIRST_PHASE;
+    }
+    return MergePhase::SECOND_PHASE;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index 092ee0b..5319e69 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -124,7 +124,6 @@
         return data_->allow_set_slot_as_unbootable();
     }
     bool IsRecovery() const override { return data_->is_recovery(); }
-    std::string GetSnapuserdFirstStagePidVar() const override { return {}; }
 
     void SwitchSlot() { switched_slot_ = !switched_slot_; }
 
diff --git a/fs_mgr/libsnapshot/snapshot_reader.cpp b/fs_mgr/libsnapshot/snapshot_reader.cpp
index b56d879..5ee8e25 100644
--- a/fs_mgr/libsnapshot/snapshot_reader.cpp
+++ b/fs_mgr/libsnapshot/snapshot_reader.cpp
@@ -90,7 +90,7 @@
     op_iter_ = cow_->GetOpIter();
     while (!op_iter_->Done()) {
         const CowOperation* op = &op_iter_->Get();
-        if (op->type == kCowLabelOp || op->type == kCowFooterOp) {
+        if (IsMetadataOp(*op)) {
             op_iter_->Next();
             continue;
         }
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 74558ab..4c209ec 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -146,6 +146,7 @@
                 "base-device",
                 "test_partition_b",
                 "test_partition_b-base",
+                "test_partition_b-base",
         };
         for (const auto& partition : partitions) {
             DeleteDevice(partition);
@@ -180,12 +181,22 @@
     }
 
     // If |path| is non-null, the partition will be mapped after creation.
-    bool CreatePartition(const std::string& name, uint64_t size, std::string* path = nullptr) {
+    bool CreatePartition(const std::string& name, uint64_t size, std::string* path = nullptr,
+                         const std::optional<std::string> group = {}) {
         TestPartitionOpener opener(fake_super);
         auto builder = MetadataBuilder::New(opener, "super", 0);
         if (!builder) return false;
 
-        auto partition = builder->AddPartition(name, 0);
+        std::string partition_group = std::string(android::fs_mgr::kDefaultGroup);
+        if (group) {
+            partition_group = *group;
+        }
+        return CreatePartition(builder.get(), name, size, path, partition_group);
+    }
+
+    bool CreatePartition(MetadataBuilder* builder, const std::string& name, uint64_t size,
+                         std::string* path, const std::string& group) {
+        auto partition = builder->AddPartition(name, group, 0);
         if (!partition) return false;
         if (!builder->ResizePartition(partition, size)) {
             return false;
@@ -194,6 +205,8 @@
         // Update the source slot.
         auto metadata = builder->Export();
         if (!metadata) return false;
+
+        TestPartitionOpener opener(fake_super);
         if (!UpdatePartitionTable(opener, "super", *metadata.get(), 0)) {
             return false;
         }
@@ -210,39 +223,55 @@
         return CreateLogicalPartition(params, path);
     }
 
-    bool MapUpdatePartitions() {
+    AssertionResult MapUpdateSnapshot(const std::string& name,
+                                      std::unique_ptr<ICowWriter>* writer) {
         TestPartitionOpener opener(fake_super);
-        auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
-        if (!builder) return false;
+        CreateLogicalPartitionParams params{
+                .block_device = fake_super,
+                .metadata_slot = 1,
+                .partition_name = name,
+                .timeout_ms = 10s,
+                .partition_opener = &opener,
+        };
 
-        auto metadata = builder->Export();
-        if (!metadata) return false;
-
-        // Update the destination slot, mark it as updated.
-        if (!UpdatePartitionTable(opener, "super", *metadata.get(), 1)) {
-            return false;
+        auto old_partition = "/dev/block/mapper/" + GetOtherPartitionName(name);
+        auto result = sm->OpenSnapshotWriter(params, {old_partition});
+        if (!result) {
+            return AssertionFailure() << "Cannot open snapshot for writing: " << name;
+        }
+        if (!result->Initialize()) {
+            return AssertionFailure() << "Cannot initialize snapshot for writing: " << name;
         }
 
-        for (const auto& partition : metadata->partitions) {
-            CreateLogicalPartitionParams params = {
-                    .block_device = fake_super,
-                    .metadata = metadata.get(),
-                    .partition = &partition,
-                    .force_writable = true,
-                    .timeout_ms = 10s,
-                    .device_name = GetPartitionName(partition) + "-base",
-            };
-            std::string ignore_path;
-            if (!CreateLogicalPartition(params, &ignore_path)) {
-                return false;
-            }
+        if (writer) {
+            *writer = std::move(result);
         }
-        return true;
+        return AssertionSuccess();
+    }
+
+    AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path) {
+        TestPartitionOpener opener(fake_super);
+        CreateLogicalPartitionParams params{
+                .block_device = fake_super,
+                .metadata_slot = 1,
+                .partition_name = name,
+                .timeout_ms = 10s,
+                .partition_opener = &opener,
+        };
+
+        auto result = sm->MapUpdateSnapshot(params, path);
+        if (!result) {
+            return AssertionFailure() << "Cannot open snapshot for writing: " << name;
+        }
+        return AssertionSuccess();
     }
 
     AssertionResult DeleteSnapshotDevice(const std::string& snapshot) {
         AssertionResult res = AssertionSuccess();
         if (!(res = DeleteDevice(snapshot))) return res;
+        if (!sm->UnmapDmUserDevice(snapshot)) {
+            return AssertionFailure() << "Cannot delete dm-user device for " << snapshot;
+        }
         if (!(res = DeleteDevice(snapshot + "-inner"))) return res;
         if (!(res = DeleteDevice(snapshot + "-cow"))) return res;
         if (!image_manager_->UnmapImageIfExists(snapshot + "-cow-img")) {
@@ -289,37 +318,55 @@
 
     // Prepare A/B slot for a partition named "test_partition".
     AssertionResult PrepareOneSnapshot(uint64_t device_size,
-                                       std::string* out_snap_device = nullptr) {
-        std::string base_device, cow_device, snap_device;
-        if (!CreatePartition("test_partition_a", device_size)) {
-            return AssertionFailure();
+                                       std::unique_ptr<ICowWriter>* writer = nullptr) {
+        lock_ = nullptr;
+
+        DeltaArchiveManifest manifest;
+
+        auto group = manifest.mutable_dynamic_partition_metadata()->add_groups();
+        group->set_name("group");
+        group->set_size(device_size * 2);
+        group->add_partition_names("test_partition");
+
+        auto pu = manifest.add_partitions();
+        pu->set_partition_name("test_partition");
+        pu->set_estimate_cow_size(device_size);
+        SetSize(pu, device_size);
+
+        auto extent = pu->add_operations()->add_dst_extents();
+        extent->set_start_block(0);
+        if (device_size) {
+            extent->set_num_blocks(device_size / manifest.block_size());
         }
-        if (!MapUpdatePartitions()) {
-            return AssertionFailure();
+
+        TestPartitionOpener opener(fake_super);
+        auto builder = MetadataBuilder::New(opener, "super", 0);
+        if (!builder) {
+            return AssertionFailure() << "Failed to open MetadataBuilder";
         }
-        if (!dm_.GetDmDevicePathByName("test_partition_b-base", &base_device)) {
-            return AssertionFailure();
+        builder->AddGroup("group_a", 16_GiB);
+        builder->AddGroup("group_b", 16_GiB);
+        if (!CreatePartition(builder.get(), "test_partition_a", device_size, nullptr, "group_a")) {
+            return AssertionFailure() << "Failed create test_partition_a";
         }
-        SnapshotStatus status;
-        status.set_name("test_partition_b");
-        status.set_device_size(device_size);
-        status.set_snapshot_size(device_size);
-        status.set_cow_file_size(device_size);
-        if (!sm->CreateSnapshot(lock_.get(), &status)) {
-            return AssertionFailure();
+
+        if (!sm->CreateUpdateSnapshots(manifest)) {
+            return AssertionFailure() << "Failed to create update snapshots";
         }
-        if (!CreateCowImage("test_partition_b")) {
-            return AssertionFailure();
+
+        if (writer) {
+            auto res = MapUpdateSnapshot("test_partition_b", writer);
+            if (!res) {
+                return res;
+            }
+        } else if (!IsCompressionEnabled()) {
+            std::string ignore;
+            if (!MapUpdateSnapshot("test_partition_b", &ignore)) {
+                return AssertionFailure() << "Failed to map test_partition_b";
+            }
         }
-        if (!MapCowImage("test_partition_b", 10s, &cow_device)) {
-            return AssertionFailure();
-        }
-        if (!sm->MapSnapshot(lock_.get(), "test_partition_b", base_device, cow_device, 10s,
-                             &snap_device)) {
-            return AssertionFailure();
-        }
-        if (out_snap_device) {
-            *out_snap_device = std::move(snap_device);
+        if (!AcquireLock()) {
+            return AssertionFailure() << "Failed to acquire lock";
         }
         return AssertionSuccess();
     }
@@ -328,20 +375,37 @@
     AssertionResult SimulateReboot() {
         lock_ = nullptr;
         if (!sm->FinishedSnapshotWrites(false)) {
-            return AssertionFailure();
+            return AssertionFailure() << "Failed to finish snapshot writes";
         }
-        if (!dm_.DeleteDevice("test_partition_b")) {
-            return AssertionFailure();
+        if (!sm->UnmapUpdateSnapshot("test_partition_b")) {
+            return AssertionFailure() << "Failed to unmap COW for test_partition_b";
         }
-        if (!DestroyLogicalPartition("test_partition_b-base")) {
-            return AssertionFailure();
+        if (!dm_.DeleteDeviceIfExists("test_partition_b")) {
+            return AssertionFailure() << "Failed to delete test_partition_b";
         }
-        if (!sm->UnmapCowImage("test_partition_b")) {
-            return AssertionFailure();
+        if (!dm_.DeleteDeviceIfExists("test_partition_b-base")) {
+            return AssertionFailure() << "Failed to destroy test_partition_b-base";
         }
         return AssertionSuccess();
     }
 
+    std::unique_ptr<SnapshotManager> NewManagerForFirstStageMount(
+            const std::string& slot_suffix = "_a") {
+        auto info = new TestDeviceInfo(fake_super, slot_suffix);
+        return NewManagerForFirstStageMount(info);
+    }
+
+    std::unique_ptr<SnapshotManager> NewManagerForFirstStageMount(TestDeviceInfo* info) {
+        auto init = SnapshotManager::NewForFirstStageMount(info);
+        if (!init) {
+            return nullptr;
+        }
+        init->SetUeventRegenCallback([](const std::string& device) -> bool {
+            return android::fs_mgr::WaitForFile(device, snapshot_timeout_);
+        });
+        return init;
+    }
+
     static constexpr std::chrono::milliseconds snapshot_timeout_ = 5s;
     DeviceMapper& dm_;
     std::unique_ptr<SnapshotManager::LockedFile> lock_;
@@ -404,31 +468,6 @@
     ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
 }
 
-TEST_F(SnapshotTest, MapPartialSnapshot) {
-    ASSERT_TRUE(AcquireLock());
-
-    static const uint64_t kSnapshotSize = 1024 * 1024;
-    static const uint64_t kDeviceSize = 1024 * 1024 * 2;
-    SnapshotStatus status;
-    status.set_name("test-snapshot");
-    status.set_device_size(kDeviceSize);
-    status.set_snapshot_size(kSnapshotSize);
-    status.set_cow_file_size(kSnapshotSize);
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
-    ASSERT_TRUE(CreateCowImage("test-snapshot"));
-
-    std::string base_device;
-    ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
-
-    std::string cow_device;
-    ASSERT_TRUE(MapCowImage("test-snapshot", 10s, &cow_device));
-
-    std::string snap_device;
-    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
-                                &snap_device));
-    ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
-}
-
 TEST_F(SnapshotTest, NoMergeBeforeReboot) {
     ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
@@ -439,8 +478,7 @@
 TEST_F(SnapshotTest, CleanFirstStageMount) {
     // If there's no update in progress, there should be no first-stage mount
     // needed.
-    TestDeviceInfo* info = new TestDeviceInfo(fake_super);
-    auto sm = SnapshotManager::NewForFirstStageMount(info);
+    auto sm = NewManagerForFirstStageMount();
     ASSERT_NE(sm, nullptr);
     ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
 }
@@ -449,8 +487,7 @@
     ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
     // We didn't change the slot, so we shouldn't need snapshots.
-    TestDeviceInfo* info = new TestDeviceInfo(fake_super);
-    auto sm = SnapshotManager::NewForFirstStageMount(info);
+    auto sm = NewManagerForFirstStageMount();
     ASSERT_NE(sm, nullptr);
     ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
 
@@ -462,32 +499,30 @@
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
-    std::string snap_device;
-    ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &snap_device));
 
-    std::string test_string = "This is a test string.";
-    {
-        unique_fd fd(open(snap_device.c_str(), O_RDWR | O_CLOEXEC | O_SYNC));
-        ASSERT_GE(fd, 0);
-        ASSERT_TRUE(android::base::WriteFully(fd, test_string.data(), test_string.size()));
-    }
-
-    // Note: we know there is no inner/outer dm device since we didn't request
-    // a linear segment.
-    DeviceMapper::TargetInfo target;
-    ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
-    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
+    std::unique_ptr<ICowWriter> writer;
+    ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer));
 
     // Release the lock.
     lock_ = nullptr;
 
+    std::string test_string = "This is a test string.";
+    test_string.resize(writer->options().block_size);
+    ASSERT_TRUE(writer->AddRawBlocks(0, test_string.data(), test_string.size()));
+    ASSERT_TRUE(writer->Finalize());
+    writer = nullptr;
+
     // Done updating.
     ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
+    ASSERT_TRUE(sm->UnmapUpdateSnapshot("test_partition_b"));
+
     test_device->set_slot_suffix("_b");
+    ASSERT_TRUE(sm->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
     ASSERT_TRUE(sm->InitiateMerge());
 
     // The device should have been switched to a snapshot-merge target.
+    DeviceMapper::TargetInfo target;
     ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
     ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
 
@@ -503,7 +538,7 @@
     // Test that we can read back the string we wrote to the snapshot. Note
     // that the base device is gone now. |snap_device| contains the correct
     // partition.
-    unique_fd fd(open(snap_device.c_str(), O_RDONLY | O_CLOEXEC));
+    unique_fd fd(open("/dev/block/mapper/test_partition_b", O_RDONLY | O_CLOEXEC));
     ASSERT_GE(fd, 0);
 
     std::string buffer(test_string.size(), '\0');
@@ -518,7 +553,7 @@
     ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
     ASSERT_TRUE(SimulateReboot());
 
-    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    auto init = NewManagerForFirstStageMount("_b");
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -531,8 +566,7 @@
     ASSERT_EQ(status.state(), SnapshotState::CREATED);
 
     DeviceMapper::TargetInfo target;
-    auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
-    ASSERT_TRUE(init->IsSnapshotDevice(dm_name, &target));
+    ASSERT_TRUE(init->IsSnapshotDevice("test_partition_b", &target));
     ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
 }
 
@@ -547,7 +581,7 @@
     FormatFakeSuper();
     ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
 
-    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    auto init = NewManagerForFirstStageMount("_b");
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -559,8 +593,7 @@
 
     // We should not get a snapshot device now.
     DeviceMapper::TargetInfo target;
-    auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
-    ASSERT_FALSE(init->IsSnapshotDevice(dm_name, &target));
+    ASSERT_FALSE(init->IsSnapshotDevice("test_partition_b", &target));
 
     // We should see a cancelled update as well.
     lock_ = nullptr;
@@ -574,7 +607,7 @@
     ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
     ASSERT_TRUE(SimulateReboot());
 
-    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    auto init = NewManagerForFirstStageMount("_b");
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -795,15 +828,15 @@
         group_->add_partition_names("prd");
         sys_ = manifest_.add_partitions();
         sys_->set_partition_name("sys");
-        sys_->set_estimate_cow_size(6_MiB);
+        sys_->set_estimate_cow_size(2_MiB);
         SetSize(sys_, 3_MiB);
         vnd_ = manifest_.add_partitions();
         vnd_->set_partition_name("vnd");
-        vnd_->set_estimate_cow_size(6_MiB);
+        vnd_->set_estimate_cow_size(2_MiB);
         SetSize(vnd_, 3_MiB);
         prd_ = manifest_.add_partitions();
         prd_->set_partition_name("prd");
-        prd_->set_estimate_cow_size(6_MiB);
+        prd_->set_estimate_cow_size(2_MiB);
         SetSize(prd_, 3_MiB);
 
         // Initialize source partition metadata using |manifest_|.
@@ -899,47 +932,7 @@
         return AssertionSuccess();
     }
 
-    AssertionResult MapUpdateSnapshot(const std::string& name,
-                                      std::unique_ptr<ICowWriter>* writer) {
-        CreateLogicalPartitionParams params{
-                .block_device = fake_super,
-                .metadata_slot = 1,
-                .partition_name = name,
-                .timeout_ms = 10s,
-                .partition_opener = opener_.get(),
-        };
-
-        auto result = sm->OpenSnapshotWriter(params, {});
-        if (!result) {
-            return AssertionFailure() << "Cannot open snapshot for writing: " << name;
-        }
-        if (!result->Initialize()) {
-            return AssertionFailure() << "Cannot initialize snapshot for writing: " << name;
-        }
-
-        if (writer) {
-            *writer = std::move(result);
-        }
-        return AssertionSuccess();
-    }
-
-    AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path) {
-        CreateLogicalPartitionParams params{
-                .block_device = fake_super,
-                .metadata_slot = 1,
-                .partition_name = name,
-                .timeout_ms = 10s,
-                .partition_opener = opener_.get(),
-        };
-
-        auto result = sm->MapUpdateSnapshot(params, path);
-        if (!result) {
-            return AssertionFailure() << "Cannot open snapshot for writing: " << name;
-        }
-        return AssertionSuccess();
-    }
-
-    AssertionResult MapUpdateSnapshot(const std::string& name) {
+    AssertionResult MapOneUpdateSnapshot(const std::string& name) {
         if (IsCompressionEnabled()) {
             std::unique_ptr<ICowWriter> writer;
             return MapUpdateSnapshot(name, &writer);
@@ -983,7 +976,7 @@
     AssertionResult MapUpdateSnapshots(const std::vector<std::string>& names = {"sys_b", "vnd_b",
                                                                                 "prd_b"}) {
         for (const auto& name : names) {
-            auto res = MapUpdateSnapshot(name);
+            auto res = MapOneUpdateSnapshot(name);
             if (!res) {
                 return res;
             }
@@ -1031,11 +1024,17 @@
         ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
     }
 
-    // Grow all partitions.
+    // Grow all partitions. Set |prd| large enough that |sys| and |vnd|'s COWs
+    // fit in super, but not |prd|.
     constexpr uint64_t partition_size = 3788_KiB;
     SetSize(sys_, partition_size);
     SetSize(vnd_, partition_size);
-    SetSize(prd_, partition_size);
+    SetSize(prd_, 18_MiB);
+
+    // Make sure |prd| does not fit in super at all. On VABC, this means we
+    // fake an extra large COW for |vnd| to fill up super.
+    vnd_->set_estimate_cow_size(30_MiB);
+    prd_->set_estimate_cow_size(30_MiB);
 
     AddOperationForPartitions();
 
@@ -1047,11 +1046,7 @@
     auto tgt = MetadataBuilder::New(*opener_, "super", 1);
     ASSERT_NE(tgt, nullptr);
     ASSERT_NE(nullptr, tgt->FindPartition("sys_b-cow"));
-    if (IsCompressionEnabled()) {
-        ASSERT_EQ(nullptr, tgt->FindPartition("vnd_b-cow"));
-    } else {
-        ASSERT_NE(nullptr, tgt->FindPartition("vnd_b-cow"));
-    }
+    ASSERT_NE(nullptr, tgt->FindPartition("vnd_b-cow"));
     ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
 
     // Write some data to target partitions.
@@ -1070,7 +1065,7 @@
     ASSERT_TRUE(UnmapAll());
 
     // After reboot, init does first stage mount.
-    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    auto init = NewManagerForFirstStageMount("_b");
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1085,6 +1080,7 @@
 
     // Initiate the merge and wait for it to be completed.
     ASSERT_TRUE(init->InitiateMerge());
+    ASSERT_EQ(init->IsSnapuserdRequired(), IsCompressionEnabled());
     ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
 
     // Check that the target partitions have the same content after the merge.
@@ -1109,6 +1105,7 @@
     SetSize(sys_, 4_MiB);  // grows
     SetSize(vnd_, 2_MiB);  // shrinks
     // prd_b is unchanged
+    ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
     ASSERT_EQ(4_MiB, GetSnapshotSize("sys_b").value_or(0));
 }
@@ -1118,6 +1115,7 @@
 TEST_F(SnapshotUpdateTest, CowPartitionDoNotTakeOldPartitions) {
     SetSize(sys_, 2_MiB);  // shrinks
     // vnd_b and prd_b are unchanged.
+    ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
 
     auto tgt = MetadataBuilder::New(*opener_, "super", 1);
@@ -1202,7 +1200,7 @@
     ASSERT_TRUE(UnmapAll());
 
     // After reboot, init does first stage mount.
-    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    auto init = NewManagerForFirstStageMount("_b");
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1214,7 +1212,7 @@
 
     // Simulate shutting down the device again.
     ASSERT_TRUE(UnmapAll());
-    init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_a"));
+    init = NewManagerForFirstStageMount("_a");
     ASSERT_NE(init, nullptr);
     ASSERT_FALSE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1241,6 +1239,11 @@
 
 // Test that at the second update, old COW partition spaces are reclaimed.
 TEST_F(SnapshotUpdateTest, ReclaimCow) {
+    // Make sure VABC cows are small enough that they fit in fake_super.
+    sys_->set_estimate_cow_size(64_KiB);
+    vnd_->set_estimate_cow_size(64_KiB);
+    prd_->set_estimate_cow_size(64_KiB);
+
     // Execute the first update.
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
@@ -1251,7 +1254,7 @@
     ASSERT_TRUE(UnmapAll());
 
     // After reboot, init does first stage mount.
-    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    auto init = NewManagerForFirstStageMount("_b");
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1357,9 +1360,13 @@
 
 TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) {
     // Make source partitions as big as possible to force COW image to be created.
-    SetSize(sys_, 5_MiB);
-    SetSize(vnd_, 5_MiB);
-    SetSize(prd_, 5_MiB);
+    SetSize(sys_, 10_MiB);
+    SetSize(vnd_, 10_MiB);
+    SetSize(prd_, 10_MiB);
+    sys_->set_estimate_cow_size(12_MiB);
+    vnd_->set_estimate_cow_size(12_MiB);
+    prd_->set_estimate_cow_size(12_MiB);
+
     src_ = MetadataBuilder::New(*opener_, "super", 0);
     ASSERT_NE(src_, nullptr);
     src_->RemoveGroupAndPartitions(group_->name() + "_a");
@@ -1387,8 +1394,8 @@
     ASSERT_TRUE(UnmapAll());
 
     // After reboot, init does first stage mount.
-    // Normally we should use NewForFirstStageMount, but if so, "gsid.mapped_image.sys_b-cow-img"
-    // won't be set.
+    // Normally we should use NewManagerForFirstStageMount, but if so,
+    // "gsid.mapped_image.sys_b-cow-img" won't be set.
     auto init = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1495,7 +1502,7 @@
     ASSERT_TRUE(UnmapAll());
 
     // After reboot, init does first stage mount.
-    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    auto init = NewManagerForFirstStageMount("_b");
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1509,7 +1516,7 @@
     // Simulate a reboot into recovery.
     auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
     test_device->set_recovery(true);
-    new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+    new_sm = NewManagerForFirstStageMount(test_device.release());
 
     ASSERT_TRUE(new_sm->HandleImminentDataWipe());
     ASSERT_EQ(new_sm->GetUpdateState(), UpdateState::None);
@@ -1527,7 +1534,7 @@
     ASSERT_TRUE(UnmapAll());
 
     // After reboot, init does first stage mount.
-    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    auto init = NewManagerForFirstStageMount("_b");
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1541,7 +1548,7 @@
     // Simulate a reboot into recovery.
     auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
     test_device->set_recovery(true);
-    new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+    new_sm = NewManagerForFirstStageMount(test_device.release());
 
     ASSERT_TRUE(new_sm->FinishMergeInRecovery());
 
@@ -1551,12 +1558,12 @@
 
     // Finish the merge in a normal boot.
     test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
-    init = SnapshotManager::NewForFirstStageMount(test_device.release());
+    init = NewManagerForFirstStageMount(test_device.release());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
     init = nullptr;
 
     test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
-    new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+    new_sm = NewManagerForFirstStageMount(test_device.release());
     ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
     ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::None);
 }
@@ -1575,7 +1582,7 @@
     // Simulate a reboot into recovery.
     auto test_device = new TestDeviceInfo(fake_super, "_b");
     test_device->set_recovery(true);
-    auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
+    auto new_sm = NewManagerForFirstStageMount(test_device);
 
     ASSERT_TRUE(new_sm->HandleImminentDataWipe());
     // Manually mount metadata so that we can call GetUpdateState() below.
@@ -1600,7 +1607,7 @@
     // Simulate a rollback, with reboot into recovery.
     auto test_device = new TestDeviceInfo(fake_super, "_a");
     test_device->set_recovery(true);
-    auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
+    auto new_sm = NewManagerForFirstStageMount(test_device);
 
     ASSERT_TRUE(new_sm->HandleImminentDataWipe());
     EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
@@ -1628,7 +1635,7 @@
     // Simulate a reboot into recovery.
     auto test_device = new TestDeviceInfo(fake_super, "_b");
     test_device->set_recovery(true);
-    auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
+    auto new_sm = NewManagerForFirstStageMount(test_device);
 
     ASSERT_TRUE(new_sm->HandleImminentDataWipe());
     // Manually mount metadata so that we can call GetUpdateState() below.
@@ -1639,7 +1646,7 @@
 
     // Now reboot into new slot.
     test_device = new TestDeviceInfo(fake_super, "_b");
-    auto init = SnapshotManager::NewForFirstStageMount(test_device);
+    auto init = NewManagerForFirstStageMount(test_device);
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
     // Verify that we are on the downgraded build.
     for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
@@ -1657,6 +1664,8 @@
     SetSize(sys_, partition_size);
     AddOperation(sys_, data_size);
 
+    sys_->set_estimate_cow_size(partition_size + data_size);
+
     // Set hastree extents.
     sys_->mutable_hash_tree_data_extent()->set_start_block(0);
     sys_->mutable_hash_tree_data_extent()->set_num_blocks(data_size / block_size);
@@ -1685,7 +1694,7 @@
     ASSERT_TRUE(UnmapAll());
 
     // After reboot, init does first stage mount.
-    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    auto init = NewManagerForFirstStageMount("_b");
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1697,6 +1706,10 @@
 
 // Test for overflow bit after update
 TEST_F(SnapshotUpdateTest, Overflow) {
+    if (IsCompressionEnabled()) {
+        GTEST_SKIP() << "No overflow bit set for userspace COWs";
+    }
+
     const auto actual_write_size = GetSize(sys_);
     const auto declared_write_size = actual_write_size - 1_MiB;
 
@@ -1724,12 +1737,15 @@
     auto userdata = std::make_unique<LowSpaceUserdata>();
     ASSERT_TRUE(userdata->Init(kMaxFree));
 
-    // Grow all partitions to 5_MiB, total 15_MiB. This requires 15 MiB of CoW space. After
-    // using the empty space in super (< 1 MiB), it uses at least 14 MiB of /userdata space.
-    constexpr uint64_t partition_size = 5_MiB;
+    // Grow all partitions to 10_MiB, total 30_MiB. This requires 30 MiB of CoW space. After
+    // using the empty space in super (< 1 MiB), it uses 30 MiB of /userdata space.
+    constexpr uint64_t partition_size = 10_MiB;
     SetSize(sys_, partition_size);
     SetSize(vnd_, partition_size);
     SetSize(prd_, partition_size);
+    sys_->set_estimate_cow_size(partition_size);
+    vnd_->set_estimate_cow_size(partition_size);
+    prd_->set_estimate_cow_size(partition_size);
 
     AddOperationForPartitions();
 
@@ -1739,7 +1755,7 @@
     ASSERT_FALSE(res);
     ASSERT_EQ(Return::ErrorCode::NO_SPACE, res.error_code());
     ASSERT_GE(res.required_size(), 14_MiB);
-    ASSERT_LT(res.required_size(), 15_MiB);
+    ASSERT_LT(res.required_size(), 40_MiB);
 }
 
 class AutoKill final {
@@ -1760,9 +1776,6 @@
         GTEST_SKIP() << "Skipping Virtual A/B Compression test";
     }
 
-    AutoKill auto_kill(StartFirstStageSnapuserd());
-    ASSERT_TRUE(auto_kill.valid());
-
     // Ensure a connection to the second-stage daemon, but use the first-stage
     // code paths thereafter.
     ASSERT_TRUE(sm->EnsureSnapuserdConnected());
@@ -1776,7 +1789,7 @@
     ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
     ASSERT_TRUE(UnmapAll());
 
-    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    auto init = NewManagerForFirstStageMount("_b");
     ASSERT_NE(init, nullptr);
 
     ASSERT_TRUE(init->EnsureSnapuserdConnected());
@@ -1788,7 +1801,7 @@
     ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow-init", F_OK), 0);
     ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), -1);
 
-    ASSERT_TRUE(init->PerformSecondStageTransition());
+    ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));
 
     // The control device should have been renamed.
     ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted("/dev/dm-user/sys_b-user-cow-init", 10s));
@@ -1890,8 +1903,7 @@
     ASSERT_TRUE(UnmapAll());
 
     // Simulate reboot. After reboot, init does first stage mount.
-    auto init = SnapshotManager::NewForFirstStageMount(
-            new TestDeviceInfo(fake_super, flashed_slot_suffix));
+    auto init = NewManagerForFirstStageMount(flashed_slot_suffix);
     ASSERT_NE(init, nullptr);
 
     if (flashed_slot && after_merge) {
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
index 954699c..9ad4a1a 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
+#include "snapuserd.h"
+
 #include <csignal>
 
-#include <libsnapshot/snapuserd.h>
 #include <libsnapshot/snapuserd_client.h>
-#include <libsnapshot/snapuserd_daemon.h>
-#include <libsnapshot/snapuserd_server.h>
 
 namespace android {
 namespace snapshot {
@@ -28,7 +27,10 @@
 using namespace android::dm;
 using android::base::unique_fd;
 
-static constexpr size_t PAYLOAD_SIZE = (1UL << 16);
+#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
+#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
+
+static constexpr size_t PAYLOAD_SIZE = (1UL << 20);
 
 static_assert(PAYLOAD_SIZE >= BLOCK_SIZE);
 
@@ -94,7 +96,7 @@
 // it will be de-compressed.
 bool Snapuserd::ProcessReplaceOp(const CowOperation* cow_op) {
     if (!reader_->ReadData(*cow_op, &bufsink_)) {
-        LOG(ERROR) << "ReadData failed for chunk: " << cow_op->new_block;
+        SNAP_LOG(ERROR) << "ReadData failed for chunk: " << cow_op->new_block;
         return false;
     }
 
@@ -111,7 +113,7 @@
     // if the successive blocks are contiguous.
     if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SIZE,
                                           cow_op->source * BLOCK_SIZE)) {
-        LOG(ERROR) << "Copy-op failed. Read from backing store at: " << cow_op->source;
+        SNAP_LOG(ERROR) << "Copy-op failed. Read from backing store at: " << cow_op->source;
         return false;
     }
 
@@ -127,104 +129,126 @@
     return true;
 }
 
+bool Snapuserd::ProcessCowOp(const CowOperation* cow_op) {
+    CHECK(cow_op != nullptr);
+
+    switch (cow_op->type) {
+        case kCowReplaceOp: {
+            return ProcessReplaceOp(cow_op);
+        }
+
+        case kCowZeroOp: {
+            return ProcessZeroOp();
+        }
+
+        case kCowCopyOp: {
+            return ProcessCopyOp(cow_op);
+        }
+
+        default: {
+            SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
+        }
+    }
+    return false;
+}
+
+int Snapuserd::ReadUnalignedSector(sector_t sector, size_t size,
+                                   std::map<sector_t, const CowOperation*>::iterator& it) {
+    size_t skip_sector_size = 0;
+
+    SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
+                    << " Aligned sector: " << it->second;
+
+    if (!ProcessCowOp(it->second)) {
+        SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed";
+        return -1;
+    }
+
+    int num_sectors_skip = sector - it->first;
+
+    if (num_sectors_skip > 0) {
+        skip_sector_size = num_sectors_skip << SECTOR_SHIFT;
+        char* buffer = reinterpret_cast<char*>(bufsink_.GetBufPtr());
+        struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
+
+        memmove(msg->payload.buf, (char*)msg->payload.buf + skip_sector_size,
+                (BLOCK_SIZE - skip_sector_size));
+    }
+
+    bufsink_.ResetBufferOffset();
+    return std::min(size, (BLOCK_SIZE - skip_sector_size));
+}
+
 /*
- * Read the data of size bytes from a given chunk.
+ * Read the data for a given COW Operation.
  *
- * Kernel can potentially merge the blocks if the
- * successive chunks are contiguous. For chunk size of 8,
- * there can be 256 disk exceptions; and if
- * all 256 disk exceptions are contiguous, kernel can merge
- * them into a single IO.
- *
- * Since each chunk in the disk exception
- * mapping represents a 4k block, kernel can potentially
- * issue 256*4k = 1M IO in one shot.
- *
- * Even though kernel assumes that the blocks are
- * contiguous, we need to split the 1M IO into 4k chunks
- * as each operation represents 4k and it can either be:
- *
- * 1: Replace operation
- * 2: Copy operation
- * 3: Zero operation
+ * Kernel can issue IO at a sector granularity.
+ * Hence, an IO may end up with reading partial
+ * data from a COW operation or we may also
+ * end up with interspersed request between
+ * two COW operations.
  *
  */
-bool Snapuserd::ReadData(chunk_t chunk, size_t size) {
-    size_t read_size = size;
-    bool ret = true;
-    chunk_t chunk_key = chunk;
-    uint32_t stride;
-    lldiv_t divresult;
-
-    // Size should always be aligned
-    CHECK((read_size & (BLOCK_SIZE - 1)) == 0);
-
-    while (read_size > 0) {
-        const CowOperation* cow_op = chunk_map_[chunk_key];
-        CHECK(cow_op != nullptr);
-
-        switch (cow_op->type) {
-            case kCowReplaceOp: {
-                ret = ProcessReplaceOp(cow_op);
-                break;
-            }
-
-            case kCowZeroOp: {
-                ret = ProcessZeroOp();
-                break;
-            }
-
-            case kCowCopyOp: {
-                ret = ProcessCopyOp(cow_op);
-                break;
-            }
-
-            default: {
-                LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
-                ret = false;
-                break;
-            }
+int Snapuserd::ReadData(sector_t sector, size_t size) {
+    /*
+     * chunk_map stores COW operation at 4k granularity.
+     * If the requested IO with the sector falls on the 4k
+     * boundary, then we can read the COW op directly without
+     * any issue.
+     *
+     * However, if the requested sector is not 4K aligned,
+     * then we will have the find the nearest COW operation
+     * and chop the 4K block to fetch the requested sector.
+     */
+    std::map<sector_t, const CowOperation*>::iterator it = chunk_map_.find(sector);
+    if (it == chunk_map_.end()) {
+        it = chunk_map_.lower_bound(sector);
+        if (it != chunk_map_.begin()) {
+            --it;
         }
 
-        if (!ret) {
-            LOG(ERROR) << "ReadData failed for operation: " << cow_op->type;
-            return false;
-        }
+        /*
+         * If the IO is spanned between two COW operations,
+         * split the IO into two parts:
+         *
+         * 1: Read the first part from the single COW op
+         * 2: Read the second part from the next COW op.
+         *
+         * Ex: Let's say we have a 1024 Bytes IO request.
+         *
+         * 0       COW OP-1  4096     COW OP-2  8192
+         * |******************|*******************|
+         *              |*****|*****|
+         *           3584           4608
+         *              <- 1024B - >
+         *
+         * We have two COW operations which are 4k blocks.
+         * The IO is requested for 1024 Bytes which are spanned
+         * between two COW operations. We will split this IO
+         * into two parts:
+         *
+         * 1: IO of size 512B from offset 3584 bytes (COW OP-1)
+         * 2: IO of size 512B from offset 4096 bytes (COW OP-2)
+         */
+        return ReadUnalignedSector(sector, size, it);
+    }
 
+    int num_ops = DIV_ROUND_UP(size, BLOCK_SIZE);
+    while (num_ops) {
+        if (!ProcessCowOp(it->second)) {
+            return -1;
+        }
+        num_ops -= 1;
+        it++;
         // Update the buffer offset
         bufsink_.UpdateBufferOffset(BLOCK_SIZE);
 
-        read_size -= BLOCK_SIZE;
-
-        // Start iterating the chunk incrementally; Since while
-        // constructing the metadata, we know that the chunk IDs
-        // are contiguous
-        chunk_key += 1;
-
-        if (cow_op->type == kCowCopyOp) CHECK(read_size == 0);
-
-        // This is similar to the way when chunk IDs were assigned
-        // in ReadMetadata().
-        //
-        // Skip if the chunk id represents a metadata chunk.
-        stride = exceptions_per_area_ + 1;
-        divresult = lldiv(chunk_key, stride);
-        if (divresult.rem == NUM_SNAPSHOT_HDR_CHUNKS) {
-            // Crossing exception boundary. Kernel will never
-            // issue IO which is spanning between a data chunk
-            // and a metadata chunk. This should be perfectly aligned.
-            //
-            // Since the input read_size is 4k aligned, we will
-            // always end up reading all 256 data chunks in one area.
-            // Thus, every multiple of 4K IO represents 256 data chunks
-            CHECK(read_size == 0);
-            break;
-        }
+        SNAP_LOG(DEBUG) << "ReadData at sector: " << sector << " size: " << size;
     }
 
     // Reset the buffer offset
     bufsink_.ResetBufferOffset();
-    return ret;
+    return size;
 }
 
 /*
@@ -275,9 +299,7 @@
     if (divresult.quot < vec_.size()) {
         size = exceptions_per_area_ * sizeof(struct disk_exception);
 
-        if (read_size > size) {
-            return false;
-        }
+        CHECK(read_size == size);
 
         void* buffer = bufsink_.GetPayloadBuffer(size);
         CHECK(buffer != nullptr);
@@ -302,8 +324,7 @@
                 reinterpret_cast<struct disk_exception*>((char*)unmerged_buffer + offset);
 
         // Unmerged op by the kernel
-        if (merged_de->old_chunk != 0) {
-            CHECK(merged_de->new_chunk != 0);
+        if (merged_de->old_chunk != 0 || merged_de->new_chunk != 0) {
             CHECK(merged_de->old_chunk == cow_de->old_chunk);
             CHECK(merged_de->new_chunk == cow_de->new_chunk);
 
@@ -312,26 +333,21 @@
             continue;
         }
 
-        // Merge complete on this exception. However, we don't know how many
-        // merged in this cycle; hence break here.
-        CHECK(merged_de->new_chunk == 0);
-        CHECK(merged_de->old_chunk == 0);
-
         break;
     }
 
     CHECK(!(*unmerged_exceptions == exceptions_per_area_));
 
-    LOG(DEBUG) << "Unmerged_Exceptions: " << *unmerged_exceptions << " Offset: " << offset;
+    SNAP_LOG(DEBUG) << "Unmerged_Exceptions: " << *unmerged_exceptions << " Offset: " << offset;
     return offset;
 }
 
 int Snapuserd::GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
-                                    int unmerged_exceptions) {
+                                    int unmerged_exceptions, bool* copy_op) {
     int merged_ops_cur_iter = 0;
 
     // Find the operations which are merged in this cycle.
-    while ((unmerged_exceptions + merged_ops_cur_iter) <= exceptions_per_area_) {
+    while ((unmerged_exceptions + merged_ops_cur_iter) < exceptions_per_area_) {
         struct disk_exception* merged_de =
                 reinterpret_cast<struct disk_exception*>((char*)merged_buffer + offset);
         struct disk_exception* cow_de =
@@ -343,6 +359,12 @@
         if (cow_de->new_chunk != 0) {
             merged_ops_cur_iter += 1;
             offset += sizeof(struct disk_exception);
+            const CowOperation* cow_op = chunk_map_[ChunkToSector(cow_de->new_chunk)];
+            CHECK(cow_op != nullptr);
+            CHECK(cow_op->new_block == cow_de->old_chunk);
+            if (cow_op->type == kCowCopyOp) {
+                *copy_op = true;
+            }
             // zero out to indicate that operation is merged.
             cow_de->old_chunk = 0;
             cow_de->new_chunk = 0;
@@ -355,85 +377,80 @@
             CHECK(cow_de->new_chunk == 0);
             break;
         } else {
-            LOG(ERROR) << "Error in merge operation. Found invalid metadata";
-            LOG(ERROR) << "merged_de-old-chunk: " << merged_de->old_chunk;
-            LOG(ERROR) << "merged_de-new-chunk: " << merged_de->new_chunk;
-            LOG(ERROR) << "cow_de-old-chunk: " << cow_de->old_chunk;
-            LOG(ERROR) << "cow_de-new-chunk: " << cow_de->new_chunk;
+            SNAP_LOG(ERROR) << "Error in merge operation. Found invalid metadata";
+            SNAP_LOG(ERROR) << "merged_de-old-chunk: " << merged_de->old_chunk;
+            SNAP_LOG(ERROR) << "merged_de-new-chunk: " << merged_de->new_chunk;
+            SNAP_LOG(ERROR) << "cow_de-old-chunk: " << cow_de->old_chunk;
+            SNAP_LOG(ERROR) << "cow_de-new-chunk: " << cow_de->new_chunk;
             return -1;
         }
     }
 
+    if (*copy_op) {
+        CHECK(merged_ops_cur_iter == 1);
+    }
     return merged_ops_cur_iter;
 }
 
-bool Snapuserd::AdvanceMergedOps(int merged_ops_cur_iter) {
-    // Advance the merge operation pointer in the
-    // vector.
-    //
-    // cowop_iter_ is already initialized in ReadMetadata(). Just resume the
-    // merge process
-    while (!cowop_iter_->Done() && merged_ops_cur_iter) {
-        const CowOperation* cow_op = &cowop_iter_->Get();
-        CHECK(cow_op != nullptr);
-
-        if (cow_op->type == kCowFooterOp || cow_op->type == kCowLabelOp) {
-            cowop_iter_->Next();
-            continue;
-        }
-
-        if (!(cow_op->type == kCowReplaceOp || cow_op->type == kCowZeroOp ||
-              cow_op->type == kCowCopyOp)) {
-            LOG(ERROR) << "Unknown operation-type found during merge: " << cow_op->type;
-            return false;
-        }
-
-        merged_ops_cur_iter -= 1;
-        LOG(DEBUG) << "Merge op found of type " << cow_op->type
-                   << "Pending-merge-ops: " << merged_ops_cur_iter;
-        cowop_iter_->Next();
-    }
-
-    if (cowop_iter_->Done()) {
-        CHECK(merged_ops_cur_iter == 0);
-        LOG(DEBUG) << "All cow operations merged successfully in this cycle";
-    }
-
-    return true;
-}
-
 bool Snapuserd::ProcessMergeComplete(chunk_t chunk, void* buffer) {
     uint32_t stride = exceptions_per_area_ + 1;
     CowHeader header;
 
     if (!reader_->GetHeader(&header)) {
-        LOG(ERROR) << "Failed to get header";
+        SNAP_LOG(ERROR) << "Failed to get header";
         return false;
     }
 
     // ChunkID to vector index
     lldiv_t divresult = lldiv(chunk, stride);
     CHECK(divresult.quot < vec_.size());
-    LOG(DEBUG) << "ProcessMergeComplete: chunk: " << chunk << " Metadata-Index: " << divresult.quot;
+    SNAP_LOG(DEBUG) << "ProcessMergeComplete: chunk: " << chunk
+                    << " Metadata-Index: " << divresult.quot;
 
     int unmerged_exceptions = 0;
     loff_t offset = GetMergeStartOffset(buffer, vec_[divresult.quot].get(), &unmerged_exceptions);
 
-    int merged_ops_cur_iter =
-            GetNumberOfMergedOps(buffer, vec_[divresult.quot].get(), offset, unmerged_exceptions);
+    bool copy_op = false;
+    // Check if the merged operation is a copy operation. If so, then we need
+    // to explicitly sync the metadata before initiating the next merge.
+    // For ex: Consider a following sequence of copy operations in the COW file:
+    //
+    // Op-1: Copy 2 -> 3
+    // Op-2: Copy 1 -> 2
+    // Op-3: Copy 5 -> 10
+    //
+    // Op-1 and Op-2 are overlapping copy operations. The merge sequence will
+    // look like:
+    //
+    // Merge op-1: Copy 2 -> 3
+    // Merge op-2: Copy 1 -> 2
+    // Merge op-3: Copy 5 -> 10
+    //
+    // Now, let's say we have a crash _after_ Merge op-2; Block 2 contents would
+    // have been over-written by Block-1 after merge op-2. During next reboot,
+    // kernel will request the metadata for all the un-merged blocks. If we had
+    // not sync the metadata after Merge-op 1 and Merge op-2, snapuser daemon
+    // will think that these merge operations are still pending and hence will
+    // inform the kernel that Op-1 and Op-2 are un-merged blocks. When kernel
+    // resumes back the merging process, it will attempt to redo the Merge op-1
+    // once again. However, block 2 contents are wrong as it has the contents
+    // of block 1 from previous merge cycle. Although, merge will silently succeed,
+    // this will lead to silent data corruption.
+    //
+    int merged_ops_cur_iter = GetNumberOfMergedOps(buffer, vec_[divresult.quot].get(), offset,
+                                                   unmerged_exceptions, &copy_op);
 
     // There should be at least one operation merged in this cycle
     CHECK(merged_ops_cur_iter > 0);
-    if (!AdvanceMergedOps(merged_ops_cur_iter)) return false;
 
     header.num_merge_ops += merged_ops_cur_iter;
     reader_->UpdateMergeProgress(merged_ops_cur_iter);
-    if (!writer_->CommitMerge(merged_ops_cur_iter)) {
-        LOG(ERROR) << "CommitMerge failed...";
+    if (!writer_->CommitMerge(merged_ops_cur_iter, copy_op)) {
+        SNAP_LOG(ERROR) << "CommitMerge failed...";
         return false;
     }
 
-    LOG(DEBUG) << "Merge success";
+    SNAP_LOG(DEBUG) << "Merge success: " << merged_ops_cur_iter << "chunk: " << chunk;
     return true;
 }
 
@@ -513,21 +530,22 @@
     bool prev_copy_op = false;
     bool metadata_found = false;
 
-    LOG(DEBUG) << "ReadMetadata Start...";
+    SNAP_LOG(DEBUG) << "ReadMetadata Start...";
 
     if (!reader_->Parse(cow_fd_)) {
-        LOG(ERROR) << "Failed to parse";
+        SNAP_LOG(ERROR) << "Failed to parse";
         return false;
     }
 
     if (!reader_->GetHeader(&header)) {
-        LOG(ERROR) << "Failed to get header";
+        SNAP_LOG(ERROR) << "Failed to get header";
         return false;
     }
 
     CHECK(header.block_size == BLOCK_SIZE);
 
-    LOG(DEBUG) << "Merge-ops: " << header.num_merge_ops;
+    SNAP_LOG(DEBUG) << "Merge-ops: " << header.num_merge_ops;
+    reader_->InitializeMerge();
 
     writer_ = std::make_unique<CowWriter>(options);
     writer_->InitializeMerge(cow_fd_.get(), &header);
@@ -539,7 +557,8 @@
 
     // Start from chunk number 2. Chunk 0 represents header and chunk 1
     // represents first metadata page.
-    chunk_t next_free = NUM_SNAPSHOT_HDR_CHUNKS + 1;
+    chunk_t data_chunk_id = NUM_SNAPSHOT_HDR_CHUNKS + 1;
+    size_t num_ops = 0;
 
     loff_t offset = 0;
     std::unique_ptr<uint8_t[]> de_ptr =
@@ -549,43 +568,34 @@
     // is 0. When Area is not filled completely with all 256 exceptions,
     // this memset will ensure that metadata read is completed.
     memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
-    size_t num_ops = 0;
 
     while (!cowop_riter_->Done()) {
         const CowOperation* cow_op = &cowop_riter_->Get();
         struct disk_exception* de =
                 reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
 
-        if (cow_op->type == kCowFooterOp || cow_op->type == kCowLabelOp) {
+        if (IsMetadataOp(*cow_op)) {
             cowop_riter_->Next();
             continue;
         }
 
-        if (!(cow_op->type == kCowReplaceOp || cow_op->type == kCowZeroOp ||
-              cow_op->type == kCowCopyOp)) {
-            LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
-            return false;
-        }
-
         metadata_found = true;
         if ((cow_op->type == kCowCopyOp || prev_copy_op)) {
-            next_free = GetNextAllocatableChunkId(next_free);
+            data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
         }
 
         prev_copy_op = (cow_op->type == kCowCopyOp);
 
         // Construct the disk-exception
         de->old_chunk = cow_op->new_block;
-        de->new_chunk = next_free;
+        de->new_chunk = data_chunk_id;
 
-        LOG(DEBUG) << "Old-chunk: " << de->old_chunk << "New-chunk: " << de->new_chunk;
+        SNAP_LOG(DEBUG) << "Old-chunk: " << de->old_chunk << "New-chunk: " << de->new_chunk;
 
         // Store operation pointer.
-        chunk_map_[next_free] = cow_op;
+        chunk_map_[ChunkToSector(data_chunk_id)] = cow_op;
         num_ops += 1;
-
         offset += sizeof(struct disk_exception);
-
         cowop_riter_->Next();
 
         if (num_ops == exceptions_per_area_) {
@@ -602,11 +612,11 @@
 
             if (cowop_riter_->Done()) {
                 vec_.push_back(std::move(de_ptr));
-                LOG(DEBUG) << "ReadMetadata() completed; Number of Areas: " << vec_.size();
+                SNAP_LOG(DEBUG) << "ReadMetadata() completed; Number of Areas: " << vec_.size();
             }
         }
 
-        next_free = GetNextAllocatableChunkId(next_free);
+        data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
     }
 
     // Partially filled area or there is no metadata
@@ -614,18 +624,15 @@
     // is aware that merge is completed.
     if (num_ops || !metadata_found) {
         vec_.push_back(std::move(de_ptr));
-        LOG(DEBUG) << "ReadMetadata() completed. Partially filled area num_ops: " << num_ops
-                   << "Areas : " << vec_.size();
+        SNAP_LOG(DEBUG) << "ReadMetadata() completed. Partially filled area num_ops: " << num_ops
+                        << "Areas : " << vec_.size();
     }
 
-    LOG(DEBUG) << "ReadMetadata() completed. chunk_id: " << next_free
-               << "Num Sector: " << ChunkToSector(next_free);
-
-    // Initialize the iterator for merging
-    cowop_iter_ = reader_->GetOpIter();
+    SNAP_LOG(DEBUG) << "ReadMetadata() completed. Final_chunk_id: " << data_chunk_id
+                    << "Num Sector: " << ChunkToSector(data_chunk_id);
 
     // Total number of sectors required for creating dm-user device
-    num_sectors_ = ChunkToSector(next_free);
+    num_sectors_ = ChunkToSector(data_chunk_id);
     metadata_read_done_ = true;
     return true;
 }
@@ -643,7 +650,7 @@
 // us the sector number for which IO is issued by dm-snapshot device
 bool Snapuserd::ReadDmUserHeader() {
     if (!android::base::ReadFully(ctrl_fd_, bufsink_.GetBufPtr(), sizeof(struct dm_user_header))) {
-        PLOG(ERROR) << "ReadDmUserHeader failed";
+        SNAP_PLOG(ERROR) << "Control-read failed";
         return false;
     }
 
@@ -654,7 +661,7 @@
 bool Snapuserd::WriteDmUserPayload(size_t size) {
     if (!android::base::WriteFully(ctrl_fd_, bufsink_.GetBufPtr(),
                                    sizeof(struct dm_user_header) + size)) {
-        PLOG(ERROR) << "Write to dm-user failed";
+        SNAP_PLOG(ERROR) << "Write to dm-user failed";
         return false;
     }
 
@@ -663,7 +670,7 @@
 
 bool Snapuserd::ReadDmUserPayload(void* buffer, size_t size) {
     if (!android::base::ReadFully(ctrl_fd_, buffer, size)) {
-        PLOG(ERROR) << "ReadDmUserPayload failed";
+        SNAP_PLOG(ERROR) << "ReadDmUserPayload failed";
         return false;
     }
 
@@ -673,7 +680,7 @@
 bool Snapuserd::InitCowDevice() {
     cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
     if (cow_fd_ < 0) {
-        PLOG(ERROR) << "Open Failed: " << cow_device_;
+        SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
         return false;
     }
 
@@ -690,151 +697,167 @@
 bool Snapuserd::InitBackingAndControlDevice() {
     backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
     if (backing_store_fd_ < 0) {
-        PLOG(ERROR) << "Open Failed: " << backing_store_device_;
+        SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
         return false;
     }
 
     ctrl_fd_.reset(open(control_device_.c_str(), O_RDWR));
     if (ctrl_fd_ < 0) {
-        PLOG(ERROR) << "Unable to open " << control_device_;
+        SNAP_PLOG(ERROR) << "Unable to open " << control_device_;
         return false;
     }
 
     return true;
 }
 
+bool Snapuserd::DmuserWriteRequest() {
+    struct dm_user_header* header = bufsink_.GetHeaderPtr();
+
+    // device mapper has the capability to allow
+    // targets to flush the cache when writes are completed. This
+    // is controlled by each target by a flag "flush_supported".
+    // This flag is set by dm-user. When flush is supported,
+    // a number of zero-length bio's will be submitted to
+    // the target for the purpose of flushing cache. It is the
+    // responsibility of the target driver - which is dm-user in this
+    // case, to remap these bio's to the underlying device. Since,
+    // there is no underlying device for dm-user, this zero length
+    // bio's gets routed to daemon.
+    //
+    // Flush operations are generated post merge by dm-snap by having
+    // REQ_PREFLUSH flag set. Snapuser daemon doesn't have anything
+    // to flush per se; hence, just respond back with a success message.
+    if (header->sector == 0) {
+        CHECK(header->len == 0);
+        header->type = DM_USER_RESP_SUCCESS;
+        if (!WriteDmUserPayload(0)) {
+            return false;
+        }
+        return true;
+    }
+
+    size_t remaining_size = header->len;
+    size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
+    CHECK(read_size == BLOCK_SIZE);
+
+    CHECK(header->sector > 0);
+    chunk_t chunk = SectorToChunk(header->sector);
+    CHECK(chunk_map_.find(header->sector) == chunk_map_.end());
+
+    void* buffer = bufsink_.GetPayloadBuffer(read_size);
+    CHECK(buffer != nullptr);
+    header->type = DM_USER_RESP_SUCCESS;
+
+    if (!ReadDmUserPayload(buffer, read_size)) {
+        SNAP_LOG(ERROR) << "ReadDmUserPayload failed for chunk id: " << chunk
+                        << "Sector: " << header->sector;
+        header->type = DM_USER_RESP_ERROR;
+    }
+
+    if (header->type == DM_USER_RESP_SUCCESS && !ProcessMergeComplete(chunk, buffer)) {
+        SNAP_LOG(ERROR) << "ProcessMergeComplete failed for chunk id: " << chunk
+                        << "Sector: " << header->sector;
+        header->type = DM_USER_RESP_ERROR;
+    } else {
+        SNAP_LOG(DEBUG) << "ProcessMergeComplete success for chunk id: " << chunk
+                        << "Sector: " << header->sector;
+    }
+
+    if (!WriteDmUserPayload(0)) {
+        return false;
+    }
+
+    return true;
+}
+
+bool Snapuserd::DmuserReadRequest() {
+    struct dm_user_header* header = bufsink_.GetHeaderPtr();
+    size_t remaining_size = header->len;
+    loff_t offset = 0;
+    sector_t sector = header->sector;
+    do {
+        size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
+
+        int ret = read_size;
+        header->type = DM_USER_RESP_SUCCESS;
+        chunk_t chunk = SectorToChunk(header->sector);
+
+        // Request to sector 0 is always for kernel
+        // representation of COW header. This IO should be only
+        // once during dm-snapshot device creation. We should
+        // never see multiple IO requests. Additionally this IO
+        // will always be a single 4k.
+        if (header->sector == 0) {
+            CHECK(metadata_read_done_ == true);
+            CHECK(read_size == BLOCK_SIZE);
+            ConstructKernelCowHeader();
+            SNAP_LOG(DEBUG) << "Kernel header constructed";
+        } else {
+            if (!offset && (read_size == BLOCK_SIZE) &&
+                chunk_map_.find(header->sector) == chunk_map_.end()) {
+                if (!ReadDiskExceptions(chunk, read_size)) {
+                    SNAP_LOG(ERROR) << "ReadDiskExceptions failed for chunk id: " << chunk
+                                    << "Sector: " << header->sector;
+                    header->type = DM_USER_RESP_ERROR;
+                } else {
+                    SNAP_LOG(DEBUG) << "ReadDiskExceptions success for chunk id: " << chunk
+                                    << "Sector: " << header->sector;
+                }
+            } else {
+                chunk_t num_sectors_read = (offset >> SECTOR_SHIFT);
+                ret = ReadData(sector + num_sectors_read, read_size);
+                if (ret < 0) {
+                    SNAP_LOG(ERROR) << "ReadData failed for chunk id: " << chunk
+                                    << "Sector: " << header->sector;
+                    header->type = DM_USER_RESP_ERROR;
+                } else {
+                    SNAP_LOG(DEBUG) << "ReadData success for chunk id: " << chunk
+                                    << "Sector: " << header->sector;
+                }
+            }
+        }
+
+        // Daemon will not be terminated if there is any error. We will
+        // just send the error back to dm-user.
+        if (!WriteDmUserPayload(ret)) {
+            return false;
+        }
+
+        remaining_size -= ret;
+        offset += ret;
+    } while (remaining_size > 0);
+
+    return true;
+}
+
 bool Snapuserd::Run() {
     struct dm_user_header* header = bufsink_.GetHeaderPtr();
 
     bufsink_.Clear();
 
     if (!ReadDmUserHeader()) {
-        LOG(ERROR) << "ReadDmUserHeader failed";
+        SNAP_LOG(ERROR) << "ReadDmUserHeader failed";
         return false;
     }
 
-    LOG(DEBUG) << "msg->seq: " << std::hex << header->seq;
-    LOG(DEBUG) << "msg->type: " << std::hex << header->type;
-    LOG(DEBUG) << "msg->flags: " << std::hex << header->flags;
-    LOG(DEBUG) << "msg->sector: " << std::hex << header->sector;
-    LOG(DEBUG) << "msg->len: " << std::hex << header->len;
+    SNAP_LOG(DEBUG) << "msg->seq: " << std::hex << header->seq;
+    SNAP_LOG(DEBUG) << "msg->type: " << std::hex << header->type;
+    SNAP_LOG(DEBUG) << "msg->flags: " << std::hex << header->flags;
+    SNAP_LOG(DEBUG) << "msg->sector: " << std::hex << header->sector;
+    SNAP_LOG(DEBUG) << "msg->len: " << std::hex << header->len;
 
     switch (header->type) {
         case DM_USER_REQ_MAP_READ: {
-            size_t remaining_size = header->len;
-            loff_t offset = 0;
-            do {
-                size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
-                header->type = DM_USER_RESP_SUCCESS;
-
-                // Request to sector 0 is always for kernel
-                // representation of COW header. This IO should be only
-                // once during dm-snapshot device creation. We should
-                // never see multiple IO requests. Additionally this IO
-                // will always be a single 4k.
-                if (header->sector == 0) {
-                    CHECK(metadata_read_done_ == true);
-                    CHECK(read_size == BLOCK_SIZE);
-                    ConstructKernelCowHeader();
-                    LOG(DEBUG) << "Kernel header constructed";
-                } else {
-                    // Convert the sector number to a chunk ID.
-                    //
-                    // Check if the chunk ID represents a metadata
-                    // page. If the chunk ID is not found in the
-                    // vector, then it points to a metadata page.
-                    chunk_t chunk = SectorToChunk(header->sector);
-
-                    if (chunk_map_.find(chunk) == chunk_map_.end()) {
-                        if (!ReadDiskExceptions(chunk, read_size)) {
-                            LOG(ERROR) << "ReadDiskExceptions failed for chunk id: " << chunk
-                                       << "Sector: " << header->sector;
-                            header->type = DM_USER_RESP_ERROR;
-                        } else {
-                            LOG(DEBUG) << "ReadDiskExceptions success for chunk id: " << chunk
-                                       << "Sector: " << header->sector;
-                        }
-                    } else {
-                        chunk_t num_chunks_read = (offset >> BLOCK_SHIFT);
-                        if (!ReadData(chunk + num_chunks_read, read_size)) {
-                            LOG(ERROR) << "ReadData failed for chunk id: " << chunk
-                                       << "Sector: " << header->sector;
-                            header->type = DM_USER_RESP_ERROR;
-                        } else {
-                            LOG(DEBUG) << "ReadData success for chunk id: " << chunk
-                                       << "Sector: " << header->sector;
-                        }
-                    }
-                }
-
-                // Daemon will not be terminated if there is any error. We will
-                // just send the error back to dm-user.
-                if (!WriteDmUserPayload(read_size)) {
-                    return false;
-                }
-
-                remaining_size -= read_size;
-                offset += read_size;
-            } while (remaining_size);
-
+            if (!DmuserReadRequest()) {
+                return false;
+            }
             break;
         }
 
         case DM_USER_REQ_MAP_WRITE: {
-            // device mapper has the capability to allow
-            // targets to flush the cache when writes are completed. This
-            // is controlled by each target by a flag "flush_supported".
-            // This flag is set by dm-user. When flush is supported,
-            // a number of zero-length bio's will be submitted to
-            // the target for the purpose of flushing cache. It is the
-            // responsibility of the target driver - which is dm-user in this
-            // case, to remap these bio's to the underlying device. Since,
-            // there is no underlying device for dm-user, this zero length
-            // bio's gets routed to daemon.
-            //
-            // Flush operations are generated post merge by dm-snap by having
-            // REQ_PREFLUSH flag set. Snapuser daemon doesn't have anything
-            // to flush per se; hence, just respond back with a success message.
-            if (header->sector == 0) {
-                CHECK(header->len == 0);
-                header->type = DM_USER_RESP_SUCCESS;
-                if (!WriteDmUserPayload(0)) {
-                    return false;
-                }
-                break;
-            }
-
-            size_t remaining_size = header->len;
-            size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
-            CHECK(read_size == BLOCK_SIZE);
-
-            CHECK(header->sector > 0);
-            chunk_t chunk = SectorToChunk(header->sector);
-            CHECK(chunk_map_.find(chunk) == chunk_map_.end());
-
-            void* buffer = bufsink_.GetPayloadBuffer(read_size);
-            CHECK(buffer != nullptr);
-            header->type = DM_USER_RESP_SUCCESS;
-
-            if (!ReadDmUserPayload(buffer, read_size)) {
-                LOG(ERROR) << "ReadDmUserPayload failed for chunk id: " << chunk
-                           << "Sector: " << header->sector;
-                header->type = DM_USER_RESP_ERROR;
-            }
-
-            if (header->type == DM_USER_RESP_SUCCESS && !ProcessMergeComplete(chunk, buffer)) {
-                LOG(ERROR) << "ProcessMergeComplete failed for chunk id: " << chunk
-                           << "Sector: " << header->sector;
-                header->type = DM_USER_RESP_ERROR;
-            } else {
-                LOG(DEBUG) << "ProcessMergeComplete success for chunk id: " << chunk
-                           << "Sector: " << header->sector;
-            }
-
-            if (!WriteDmUserPayload(0)) {
+            if (!DmuserWriteRequest()) {
                 return false;
             }
-
             break;
         }
     }
@@ -844,18 +867,3 @@
 
 }  // namespace snapshot
 }  // namespace android
-
-int main([[maybe_unused]] int argc, char** argv) {
-    android::base::InitLogging(argv, &android::base::KernelLogger);
-
-    android::snapshot::Daemon& daemon = android::snapshot::Daemon::Instance();
-
-    std::string socket = android::snapshot::kSnapuserdSocket;
-    if (argc >= 2) {
-        socket = argv[1];
-    }
-    daemon.StartServer(socket);
-    daemon.Run();
-
-    return 0;
-}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/snapuserd.h
similarity index 81%
rename from fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
rename to fs_mgr/libsnapshot/snapuserd.h
index 24b44fa..c01fee3 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd.h
@@ -22,9 +22,9 @@
 #include <cstring>
 #include <iostream>
 #include <limits>
+#include <map>
 #include <string>
 #include <thread>
-#include <unordered_map>
 #include <vector>
 
 #include <android-base/file.h>
@@ -72,6 +72,9 @@
     bool IsAttached() const { return ctrl_fd_ >= 0; }
 
   private:
+    bool DmuserReadRequest();
+    bool DmuserWriteRequest();
+
     bool ReadDmUserHeader();
     bool ReadDmUserPayload(void* buffer, size_t size);
     bool WriteDmUserPayload(size_t size);
@@ -79,10 +82,13 @@
     bool ReadMetadata();
     bool ZerofillDiskExceptions(size_t read_size);
     bool ReadDiskExceptions(chunk_t chunk, size_t size);
-    bool ReadData(chunk_t chunk, size_t size);
+    int ReadUnalignedSector(sector_t sector, size_t size,
+                            std::map<sector_t, const CowOperation*>::iterator& it);
+    int ReadData(sector_t sector, size_t size);
     bool IsChunkIdMetadata(chunk_t chunk);
-    chunk_t GetNextAllocatableChunkId(chunk_t chunk);
+    chunk_t GetNextAllocatableChunkId(chunk_t chunk_id);
 
+    bool ProcessCowOp(const CowOperation* cow_op);
     bool ProcessReplaceOp(const CowOperation* cow_op);
     bool ProcessCopyOp(const CowOperation* cow_op);
     bool ProcessZeroOp();
@@ -90,11 +96,11 @@
     loff_t GetMergeStartOffset(void* merged_buffer, void* unmerged_buffer,
                                int* unmerged_exceptions);
     int GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
-                             int unmerged_exceptions);
-    bool AdvanceMergedOps(int merged_ops_cur_iter);
+                             int unmerged_exceptions, bool* copy_op);
     bool ProcessMergeComplete(chunk_t chunk, void* buffer);
     sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
     chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
+    bool IsBlockAligned(int read_size) { return ((read_size & (BLOCK_SIZE - 1)) == 0); }
 
     std::string cow_device_;
     std::string backing_store_device_;
@@ -117,9 +123,15 @@
     // mapping of old-chunk to new-chunk
     std::vector<std::unique_ptr<uint8_t[]>> vec_;
 
-    // Key - Chunk ID
+    // Key - Sector
     // Value - cow operation
-    std::unordered_map<chunk_t, const CowOperation*> chunk_map_;
+    //
+    // chunk_map stores the pseudo mapping of sector
+    // to COW operations. Each COW op is 4k; however,
+    // we can get a read request which are as small
+    // as 512 bytes. Hence, we need to binary search
+    // in the chunk_map to find the nearest COW op.
+    std::map<sector_t, const CowOperation*> chunk_map_;
 
     bool metadata_read_done_ = false;
     BufferSink bufsink_;
diff --git a/fs_mgr/libsnapshot/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd_client.cpp
index a5d2061..16d02e4 100644
--- a/fs_mgr/libsnapshot/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_client.cpp
@@ -54,29 +54,10 @@
     return true;
 }
 
-pid_t StartFirstStageSnapuserd() {
-    pid_t pid = fork();
-    if (pid < 0) {
-        PLOG(ERROR) << "fork failed";
-        return pid;
-    }
-    if (pid != 0) {
-        return pid;
-    }
-
-    std::string arg0 = "/system/bin/snapuserd";
-    std::string arg1 = kSnapuserdSocketFirstStage;
-    char* const argv[] = {arg0.data(), arg1.data(), nullptr};
-    if (execv(arg0.c_str(), argv) < 0) {
-        PLOG(FATAL) << "execv failed";
-    }
-    return pid;
-}
-
 SnapuserdClient::SnapuserdClient(android::base::unique_fd&& sockfd) : sockfd_(std::move(sockfd)) {}
 
 static inline bool IsRetryErrno() {
-    return errno == ECONNREFUSED || errno == EINTR;
+    return errno == ECONNREFUSED || errno == EINTR || errno == ENOENT;
 }
 
 std::unique_ptr<SnapuserdClient> SnapuserdClient::Connect(const std::string& socket_name,
@@ -131,6 +112,7 @@
 }
 
 bool SnapuserdClient::Sendmsg(const std::string& msg) {
+    LOG(DEBUG) << "Sendmsg: msg " << msg << " sockfd: " << sockfd_;
     ssize_t numBytesSent = TEMP_FAILURE_RETRY(send(sockfd_, msg.data(), msg.size(), 0));
     if (numBytesSent < 0) {
         PLOG(ERROR) << "Send failed";
@@ -229,5 +211,13 @@
     return num_sectors;
 }
 
+bool SnapuserdClient::DetachSnapuserd() {
+    if (!Sendmsg("detach")) {
+        LOG(ERROR) << "Failed to detach snapuserd.";
+        return false;
+    }
+    return true;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd_daemon.cpp
index 4c8fa57..7fa01b7 100644
--- a/fs_mgr/libsnapshot/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_daemon.cpp
@@ -14,17 +14,44 @@
  * limitations under the License.
  */
 
+#include "snapuserd_daemon.h"
+
 #include <android-base/logging.h>
-#include <libsnapshot/snapuserd_daemon.h>
+#include <android-base/strings.h>
+#include <gflags/gflags.h>
+#include <libsnapshot/snapuserd_client.h>
+
+#include "snapuserd_server.h"
+
+DEFINE_string(socket, android::snapshot::kSnapuserdSocket, "Named socket or socket path.");
+DEFINE_bool(no_socket, false,
+            "If true, no socket is used. Each additional argument is an INIT message.");
 
 namespace android {
 namespace snapshot {
 
-bool Daemon::StartServer(const std::string& socketname) {
-    if (!server_.Start(socketname)) {
-        LOG(ERROR) << "Snapuserd daemon failed to start...";
-        exit(EXIT_FAILURE);
+bool Daemon::StartServer(int argc, char** argv) {
+    int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+    if (!FLAGS_no_socket) {
+        return server_.Start(FLAGS_socket);
     }
+
+    for (int i = arg_start; i < argc; i++) {
+        auto parts = android::base::Split(argv[i], ",");
+        if (parts.size() != 3) {
+            LOG(ERROR) << "Malformed message, expected three sub-arguments.";
+            return false;
+        }
+        auto handler = server_.AddHandler(parts[0], parts[1], parts[2]);
+        if (!handler || !server_.StartHandler(handler)) {
+            return false;
+        }
+    }
+
+    // Skip the accept() call to avoid spurious log spam. The server will still
+    // run until all handlers have completed.
+    server_.SetTerminating();
     return true;
 }
 
@@ -89,3 +116,17 @@
 
 }  // namespace snapshot
 }  // namespace android
+
+int main(int argc, char** argv) {
+    android::base::InitLogging(argv, &android::base::KernelLogger);
+
+    android::snapshot::Daemon& daemon = android::snapshot::Daemon::Instance();
+
+    if (!daemon.StartServer(argc, argv)) {
+        LOG(ERROR) << "Snapuserd daemon failed to start.";
+        exit(EXIT_FAILURE);
+    }
+    daemon.Run();
+
+    return 0;
+}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h b/fs_mgr/libsnapshot/snapuserd_daemon.h
similarity index 91%
rename from fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h
rename to fs_mgr/libsnapshot/snapuserd_daemon.h
index c6779b8..f8afac5 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h
+++ b/fs_mgr/libsnapshot/snapuserd_daemon.h
@@ -16,7 +16,10 @@
 
 #include <poll.h>
 
-#include <libsnapshot/snapuserd_server.h>
+#include <string>
+#include <vector>
+
+#include "snapuserd_server.h"
 
 namespace android {
 namespace snapshot {
@@ -32,7 +35,7 @@
         return instance;
     }
 
-    bool StartServer(const std::string& socketname);
+    bool StartServer(int argc, char** argv);
     void Run();
     void Interrupt();
 
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd_server.cpp
index 9d57ab0..38abaec 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_server.cpp
@@ -26,8 +26,9 @@
 #include <unistd.h>
 
 #include <android-base/logging.h>
-#include <libsnapshot/snapuserd.h>
-#include <libsnapshot/snapuserd_server.h>
+
+#include "snapuserd.h"
+#include "snapuserd_server.h"
 
 namespace android {
 namespace snapshot {
@@ -38,6 +39,7 @@
     if (input == "stop") return DaemonOperations::STOP;
     if (input == "query") return DaemonOperations::QUERY;
     if (input == "delete") return DaemonOperations::DELETE;
+    if (input == "detach") return DaemonOperations::DETACH;
 
     return DaemonOperations::INVALID;
 }
@@ -72,24 +74,11 @@
 
 void SnapuserdServer::ShutdownThreads() {
     StopThreads();
-
-    // Acquire the thread list within the lock.
-    std::vector<std::shared_ptr<DmUserHandler>> dm_users;
-    {
-        std::lock_guard<std::mutex> guard(lock_);
-        dm_users = std::move(dm_users_);
-    }
-
-    for (auto& client : dm_users) {
-        auto& th = client->thread();
-
-        if (th.joinable()) th.join();
-    }
+    JoinAllThreads();
 }
 
-const std::string& DmUserHandler::GetMiscName() const {
-    return snapuserd_->GetMiscName();
-}
+DmUserHandler::DmUserHandler(std::unique_ptr<Snapuserd>&& snapuserd)
+    : snapuserd_(std::move(snapuserd)), misc_name_(snapuserd_->GetMiscName()) {}
 
 bool SnapuserdServer::Sendmsg(android::base::borrowed_fd fd, const std::string& msg) {
     ssize_t ret = TEMP_FAILURE_RETRY(send(fd.get(), msg.data(), msg.size(), 0));
@@ -126,7 +115,7 @@
     switch (op) {
         case DaemonOperations::INIT: {
             // Message format:
-            // init,<misc_name>,<cow_device_path>,<control_device>
+            // init,<misc_name>,<cow_device_path>,<backing_device>
             //
             // Reads the metadata and send the number of sectors
             if (out.size() != 4) {
@@ -134,24 +123,12 @@
                 return Sendmsg(fd, "fail");
             }
 
-            auto snapuserd = std::make_unique<Snapuserd>(out[1], out[2], out[3]);
-            if (!snapuserd->InitCowDevice()) {
-                LOG(ERROR) << "Failed to initialize Snapuserd";
+            auto handler = AddHandler(out[1], out[2], out[3]);
+            if (!handler) {
                 return Sendmsg(fd, "fail");
             }
 
-            std::string retval = "success," + std::to_string(snapuserd->GetNumSectors());
-
-            auto handler = std::make_unique<DmUserHandler>(std::move(snapuserd));
-            {
-                std::lock_guard<std::mutex> lock(lock_);
-                if (FindHandler(&lock, out[1]) != dm_users_.end()) {
-                    LOG(ERROR) << "Handler already exists: " << out[1];
-                    return Sendmsg(fd, "fail");
-                }
-                dm_users_.push_back(std::move(handler));
-            }
-
+            auto retval = "success," + std::to_string(handler->snapuserd()->GetNumSectors());
             return Sendmsg(fd, retval);
         }
         case DaemonOperations::START: {
@@ -170,15 +147,13 @@
                 LOG(ERROR) << "Could not find handler: " << out[1];
                 return Sendmsg(fd, "fail");
             }
-            if ((*iter)->snapuserd()->IsAttached()) {
+            if (!(*iter)->snapuserd() || (*iter)->snapuserd()->IsAttached()) {
                 LOG(ERROR) << "Tried to re-attach control device: " << out[1];
                 return Sendmsg(fd, "fail");
             }
-            if (!((*iter)->snapuserd()->InitBackingAndControlDevice())) {
-                LOG(ERROR) << "Failed to initialize control device: " << out[1];
+            if (!StartHandler(*iter)) {
                 return Sendmsg(fd, "fail");
             }
-            (*iter)->thread() = std::thread(std::bind(&SnapuserdServer::RunThread, this, *iter));
             return Sendmsg(fd, "success");
         }
         case DaemonOperations::STOP: {
@@ -209,11 +184,15 @@
                 LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
                 return Sendmsg(fd, "fail");
             }
-            if (!RemoveHandler(out[1], true)) {
+            if (!RemoveAndJoinHandler(out[1])) {
                 return Sendmsg(fd, "fail");
             }
             return Sendmsg(fd, "success");
         }
+        case DaemonOperations::DETACH: {
+            terminating_ = true;
+            return Sendmsg(fd, "success");
+        }
         default: {
             LOG(ERROR) << "Received unknown message type from client";
             Sendmsg(fd, "fail");
@@ -223,20 +202,38 @@
 }
 
 void SnapuserdServer::RunThread(std::shared_ptr<DmUserHandler> handler) {
-    LOG(INFO) << "Entering thread for handler: " << handler->GetMiscName();
+    LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
 
     while (!StopRequested()) {
         if (!handler->snapuserd()->Run()) {
-            LOG(INFO) << "Snapuserd: Thread terminating";
             break;
         }
     }
 
-    LOG(INFO) << "Exiting thread for handler: " << handler->GetMiscName();
+    auto misc_name = handler->misc_name();
+    LOG(INFO) << "Handler thread about to exit: " << misc_name;
 
-    // If the main thread called /emoveHandler, the handler was already removed
-    // from within the lock, and calling RemoveHandler again has no effect.
-    RemoveHandler(handler->GetMiscName(), false);
+    {
+        std::lock_guard<std::mutex> lock(lock_);
+        auto iter = FindHandler(&lock, handler->misc_name());
+        if (iter == dm_users_.end()) {
+            // RemoveAndJoinHandler() already removed us from the list, and is
+            // now waiting on a join(), so just return.
+            LOG(INFO) << "Exiting handler thread to allow for join: " << misc_name;
+            return;
+        }
+
+        LOG(INFO) << "Exiting handler thread and freeing resources: " << misc_name;
+
+        if (handler->snapuserd()->IsAttached()) {
+            handler->thread().detach();
+        }
+
+        // Important: free resources within the lock. This ensures that if
+        // WaitForDelete() is called, the handler is either in the list, or
+        // it's not and its resources are guaranteed to be freed.
+        handler->FreeResources();
+    }
 }
 
 bool SnapuserdServer::Start(const std::string& socketname) {
@@ -286,9 +283,26 @@
             }
         }
     }
+
+    JoinAllThreads();
     return true;
 }
 
+void SnapuserdServer::JoinAllThreads() {
+    // Acquire the thread list within the lock.
+    std::vector<std::shared_ptr<DmUserHandler>> dm_users;
+    {
+        std::lock_guard<std::mutex> guard(lock_);
+        dm_users = std::move(dm_users_);
+    }
+
+    for (auto& client : dm_users) {
+        auto& th = client->thread();
+
+        if (th.joinable()) th.join();
+    }
+}
+
 void SnapuserdServer::AddWatchedFd(android::base::borrowed_fd fd) {
     struct pollfd p = {};
     p.fd = fd.get();
@@ -329,19 +343,52 @@
     SetTerminating();
 }
 
+std::shared_ptr<DmUserHandler> SnapuserdServer::AddHandler(const std::string& misc_name,
+                                                           const std::string& cow_device_path,
+                                                           const std::string& backing_device) {
+    auto snapuserd = std::make_unique<Snapuserd>(misc_name, cow_device_path, backing_device);
+    if (!snapuserd->InitCowDevice()) {
+        LOG(ERROR) << "Failed to initialize Snapuserd";
+        return nullptr;
+    }
+
+    auto handler = std::make_shared<DmUserHandler>(std::move(snapuserd));
+    {
+        std::lock_guard<std::mutex> lock(lock_);
+        if (FindHandler(&lock, misc_name) != dm_users_.end()) {
+            LOG(ERROR) << "Handler already exists: " << misc_name;
+            return nullptr;
+        }
+        dm_users_.push_back(handler);
+    }
+    return handler;
+}
+
+bool SnapuserdServer::StartHandler(const std::shared_ptr<DmUserHandler>& handler) {
+    CHECK(!handler->snapuserd()->IsAttached());
+
+    if (!handler->snapuserd()->InitBackingAndControlDevice()) {
+        LOG(ERROR) << "Failed to initialize control device: " << handler->misc_name();
+        return false;
+    }
+
+    handler->thread() = std::thread(std::bind(&SnapuserdServer::RunThread, this, handler));
+    return true;
+}
+
 auto SnapuserdServer::FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
                                   const std::string& misc_name) -> HandlerList::iterator {
     CHECK(proof_of_lock);
 
     for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
-        if ((*iter)->GetMiscName() == misc_name) {
+        if ((*iter)->misc_name() == misc_name) {
             return iter;
         }
     }
     return dm_users_.end();
 }
 
-bool SnapuserdServer::RemoveHandler(const std::string& misc_name, bool wait) {
+bool SnapuserdServer::RemoveAndJoinHandler(const std::string& misc_name) {
     std::shared_ptr<DmUserHandler> handler;
     {
         std::lock_guard<std::mutex> lock(lock_);
@@ -356,10 +403,8 @@
     }
 
     auto& th = handler->thread();
-    if (th.joinable() && wait) {
+    if (th.joinable()) {
         th.join();
-    } else if (handler->snapuserd()->IsAttached()) {
-        th.detach();
     }
     return true;
 }
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd_server.h
similarity index 83%
rename from fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
rename to fs_mgr/libsnapshot/snapuserd_server.h
index cadfd71..7cbc2de 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd_server.h
@@ -28,7 +28,7 @@
 #include <vector>
 
 #include <android-base/unique_fd.h>
-#include <libsnapshot/snapuserd.h>
+#include "snapuserd.h"
 
 namespace android {
 namespace snapshot {
@@ -41,22 +41,24 @@
     QUERY,
     STOP,
     DELETE,
+    DETACH,
     INVALID,
 };
 
 class DmUserHandler {
-  private:
-    std::thread thread_;
-    std::unique_ptr<Snapuserd> snapuserd_;
-
   public:
-    explicit DmUserHandler(std::unique_ptr<Snapuserd>&& snapuserd)
-        : snapuserd_(std::move(snapuserd)) {}
+    explicit DmUserHandler(std::unique_ptr<Snapuserd>&& snapuserd);
 
+    void FreeResources() { snapuserd_ = nullptr; }
     const std::unique_ptr<Snapuserd>& snapuserd() const { return snapuserd_; }
     std::thread& thread() { return thread_; }
 
-    const std::string& GetMiscName() const;
+    const std::string& misc_name() const { return misc_name_; }
+
+  private:
+    std::thread thread_;
+    std::unique_ptr<Snapuserd> snapuserd_;
+    std::string misc_name_;
 };
 
 class Stoppable {
@@ -70,8 +72,9 @@
 
     bool StopRequested() {
         // checks if value in future object is available
-        if (futureObj_.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout)
+        if (futureObj_.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout) {
             return false;
+        }
         return true;
     }
     // Request the thread to stop by setting value in promise object
@@ -97,15 +100,15 @@
     bool Receivemsg(android::base::borrowed_fd fd, const std::string& str);
 
     void ShutdownThreads();
-    bool RemoveHandler(const std::string& control_device, bool wait);
+    bool RemoveAndJoinHandler(const std::string& control_device);
     DaemonOperations Resolveop(std::string& input);
     std::string GetDaemonStatus();
     void Parsemsg(std::string const& msg, const char delim, std::vector<std::string>& out);
 
-    void SetTerminating() { terminating_ = true; }
     bool IsTerminating() { return terminating_; }
 
     void RunThread(std::shared_ptr<DmUserHandler> handler);
+    void JoinAllThreads();
 
     // Find a DmUserHandler within a lock.
     HandlerList::iterator FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
@@ -118,6 +121,13 @@
     bool Start(const std::string& socketname);
     bool Run();
     void Interrupt();
+
+    std::shared_ptr<DmUserHandler> AddHandler(const std::string& misc_name,
+                                              const std::string& cow_device_path,
+                                              const std::string& backing_device);
+    bool StartHandler(const std::shared_ptr<DmUserHandler>& handler);
+
+    void SetTerminating() { terminating_ = true; }
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 7342fd4..4a2af1c 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -187,5 +187,13 @@
     return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
 }
 
+std::string GetOtherPartitionName(const std::string& name) {
+    auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);
+    CHECK(suffix == "_a" || suffix == "_b");
+
+    auto other_suffix = (suffix == "_a") ? "_b" : "_a";
+    return name.substr(0, name.size() - suffix.size()) + other_suffix;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 3e6873b..671de9d 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -131,5 +131,8 @@
 
 bool IsCompressionEnabled();
 
+// Swap the suffix of a partition name.
+std::string GetOtherPartitionName(const std::string& name);
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libvbmeta/Android.bp b/fs_mgr/libvbmeta/Android.bp
index bceabab..c882e51 100644
--- a/fs_mgr/libvbmeta/Android.bp
+++ b/fs_mgr/libvbmeta/Android.bp
@@ -50,4 +50,7 @@
         "avbtool",
         "vbmake",
     ],
+    data: [
+        "data/*",
+    ],
 }
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/data/testkey_rsa2048.pem b/fs_mgr/libvbmeta/data/testkey_rsa2048.pem
new file mode 100644
index 0000000..867dcff
--- /dev/null
+++ b/fs_mgr/libvbmeta/data/testkey_rsa2048.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAxlVR3TIkouAOvH79vaJTgFhpfvVKQIeVkFRZPVXK/zY0Gvrh
+4JAqGjJoW/PfrQv5sdD36qtHH3a+G5hLZ6Ni+t/mtfjucxZfuLGC3kmJ1T3XqEKZ
+gXXI2IR7vVSoImREvDQGEDyJwtHzLANlkbGg0cghVhWZSCAndO8BenalC2v94/rt
+DfkPekH6dgU3Sf40T0sBSeSY94mOzTaqOR2pfV1rWlLRdWmo33zeHBv52Rlbt0dM
+uXAureXWiHztkm5GCBC1dgM+CaxNtizNEgC91KcD0xuRCCM2WxH+r1lpszyIJDct
+YbrFmVEYl/kjQpafhy7Nsk1fqSTyRdriZSYmTQIDAQABAoIBAQC+kJgaCuX8wYAn
+SXWQ0fmdZlXnMNRpcF0a0pD0SAzGb1RdYBXMaXiqtyhiwc53PPxsCDdNecjayIMd
+jJVXPTwLhTruOgMS/bp3gcgWwV34UHV4LJXGOGAE+jbS0hbDBMiudOYmj6RmVshp
+z9G1zZCSQNMXHaWsEYkX59XpzzoB384nRul2QgEtwzUNR9XlpzgtJBLk3SACkvsN
+mQ/DW8IWHXLg8vLn1LzVJ2e3B16H4MoE2TCHxqfMgr03IDRRJogkenQuQsFhevYT
+o/mJyHSWavVgzMHG9I5m+eepF4Wyhj1Y4WyKAuMI+9dHAX/h7Lt8XFCQCh5DbkVG
+zGr34sWBAoGBAOs7n7YZqNaaguovfIdRRsxxZr1yJAyDsr6w3yGImDZYju4c4WY9
+5esO2kP3FA4p0c7FhQF5oOb1rBuHEPp36cpL4aGeK87caqTfq63WZAujoTZpr9Lp
+BRbkL7w/xG7jpQ/clpA8sHzHGQs/nelxoOtC7E118FiRgvD/jdhlMyL9AoGBANfX
+vyoN1pplfT2xR8QOjSZ+Q35S/+SAtMuBnHx3l0qH2bbBjcvM1MNDWjnRDyaYhiRu
+i+KA7tqfib09+XpB3g5D6Ov7ls/Ldx0S/VcmVWtia2HK8y8iLGtokoBZKQ5AaFX2
+iQU8+tC4h69GnJYQKqNwgCUzh8+gHX5Y46oDiTmRAoGAYpOx8lX+czB8/Da6MNrW
+mIZNT8atZLEsDs2ANEVRxDSIcTCZJId7+m1W+nRoaycLTWNowZ1+2ErLvR10+AGY
+b7Ys79Wg9idYaY9yGn9lnZsMzAiuLeyIvXcSqgjvAKlVWrhOQFOughvNWvFl85Yy
+oWSCMlPiTLtt7CCsCKsgKuECgYBgdIp6GZsIfkgclKe0hqgvRoeU4TR3gcjJlM9A
+lBTo+pKhaBectplx9RxR8AnsPobbqwcaHnIfAuKDzjk5mEvKZjClnFXF4HAHbyAF
+nRzZEy9XkWFhc80T5rRpZO7C7qdxmu2aiKixM3V3L3/0U58qULEDbubHMw9bEhAT
+PudI8QKBgHEEiMm/hr9T41hbQi/LYanWnlFw1ue+osKuF8bXQuxnnHNuFT/c+9/A
+vWhgqG6bOEHu+p/IPrYm4tBMYlwsyh4nXCyGgDJLbLIfzKwKAWCtH9LwnyDVhOow
+GH9shdR+sW3Ew97xef02KAH4VlNANEmBV4sQNqWWvsYrcFm2rOdL
+-----END RSA PRIVATE KEY-----
diff --git a/fs_mgr/libvbmeta/super_vbmeta_test.cpp b/fs_mgr/libvbmeta/super_vbmeta_test.cpp
index daed0d1..7329a61 100644
--- a/fs_mgr/libvbmeta/super_vbmeta_test.cpp
+++ b/fs_mgr/libvbmeta/super_vbmeta_test.cpp
@@ -54,7 +54,7 @@
     cmd << "avbtool add_hashtree_footer"
         << " --image " << file_name << " --partition_name " << partition_name
         << " --partition_size " << FAKE_PARTITION_SIZE << " --algorithm SHA256_RSA2048"
-        << " --key external/avb/test/data/testkey_rsa2048.pem";
+        << " --key data/testkey_rsa2048.pem";
 
     int rc = system(cmd.str().c_str());
     EXPECT_TRUE(WIFEXITED(rc));
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index f5bbe35..2433833 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -740,7 +740,7 @@
   grep -v \
     -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\) " \
     -e "^\(binfmt_misc\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
-    -e "^\(ramdumpfs\|binder\|/sys/kernel/debug\) " \
+    -e "^\(ramdumpfs\|binder\|/sys/kernel/debug\|securityfs\) " \
     -e " functionfs " \
     -e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
     -e "^rootfs / rootfs rw," \
diff --git a/fs_mgr/tools/dmuserd.cpp b/fs_mgr/tools/dmuserd.cpp
index 92f5878..e50a4a2 100644
--- a/fs_mgr/tools/dmuserd.cpp
+++ b/fs_mgr/tools/dmuserd.cpp
@@ -76,7 +76,7 @@
 
 static bool verbose = false;
 
-size_t write_all(int fd, void* buf, size_t len) {
+ssize_t write_all(int fd, void* buf, size_t len) {
     char* buf_c = (char*)buf;
     ssize_t total = 0;
     ssize_t once;
@@ -94,7 +94,7 @@
     return total;
 }
 
-size_t read_all(int fd, void* buf, size_t len) {
+ssize_t read_all(int fd, void* buf, size_t len) {
     char* buf_c = (char*)buf;
     ssize_t total = 0;
     ssize_t once;
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 27a6452..2d9a820 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -28,6 +28,7 @@
 
     shared_libs: [
         "libbinder",
+        "libbinder_ndk",
         "libgatekeeper",
         "libgsi",
         "liblog",
@@ -40,6 +41,8 @@
         "libhidlbase",
         "android.hardware.gatekeeper@1.0",
         "libgatekeeper_aidl",
+        "android.hardware.security.keymint-unstable-ndk_platform",
+        "android.security.authorization-ndk_platform",
     ],
 
     static_libs: ["libscrypt_static"],
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index b982dbc..941f8c2 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -19,42 +19,45 @@
 #include <android/service/gatekeeper/BnGateKeeperService.h>
 #include <gatekeeper/GateKeeperResponse.h>
 
+#include <endian.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <inttypes.h>
-#include <stdint.h>
 #include <unistd.h>
 #include <memory>
 
-#include <android/security/keystore/IKeystoreService.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <android/binder_ibinder.h>
+#include <android/binder_manager.h>
+#include <android/security/keystore/IKeystoreService.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
-#include <gatekeeper/password_handle.h> // for password_handle_t
-#include <hardware/gatekeeper.h>
+#include <gatekeeper/password_handle.h>  // for password_handle_t
 #include <hardware/hw_auth_token.h>
-#include <keystore/keystore.h> // For error code
 #include <keystore/keystore_return_types.h>
 #include <libgsi/libgsi.h>
 #include <log/log.h>
-#include <utils/Log.h>
 #include <utils/String16.h>
 
-#include <hidl/HidlSupport.h>
+#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
+#include <aidl/android/security/authorization/IKeystoreAuthorization.h>
 #include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+#include <hidl/HidlSupport.h>
 
 using android::sp;
-using android::hardware::gatekeeper::V1_0::IGatekeeper;
-using android::hardware::gatekeeper::V1_0::GatekeeperStatusCode;
-using android::hardware::gatekeeper::V1_0::GatekeeperResponse;
 using android::hardware::Return;
+using android::hardware::gatekeeper::V1_0::GatekeeperResponse;
+using android::hardware::gatekeeper::V1_0::GatekeeperStatusCode;
+using android::hardware::gatekeeper::V1_0::IGatekeeper;
 
 using ::android::binder::Status;
 using ::android::service::gatekeeper::BnGateKeeperService;
 using GKResponse = ::android::service::gatekeeper::GateKeeperResponse;
 using GKResponseCode = ::android::service::gatekeeper::ResponseCode;
+using ::aidl::android::hardware::security::keymint::HardwareAuthenticatorType;
+using ::aidl::android::hardware::security::keymint::HardwareAuthToken;
+using ::aidl::android::security::authorization::IKeystoreAuthorization;
 
 namespace android {
 
@@ -62,7 +65,7 @@
 static const String16 DUMP_PERMISSION("android.permission.DUMP");
 
 class GateKeeperProxy : public BnGateKeeperService {
-public:
+  public:
     GateKeeperProxy() {
         clear_state_if_needed_done = false;
         hw_device = IGatekeeper::getService();
@@ -73,8 +76,7 @@
         }
     }
 
-    virtual ~GateKeeperProxy() {
-    }
+    virtual ~GateKeeperProxy() {}
 
     void store_sid(uint32_t userId, uint64_t sid) {
         char filename[21];
@@ -96,7 +98,7 @@
         if (mark_cold_boot() && !is_running_gsi) {
             ALOGI("cold boot: clearing state");
             if (hw_device) {
-                hw_device->deleteAllUsers([](const GatekeeperResponse &){});
+                hw_device->deleteAllUsers([](const GatekeeperResponse&) {});
             }
         }
 
@@ -104,7 +106,7 @@
     }
 
     bool mark_cold_boot() {
-        const char *filename = ".coldboot";
+        const char* filename = ".coldboot";
         if (access(filename, F_OK) == -1) {
             int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
             if (fd < 0) {
@@ -299,7 +301,36 @@
 
         if (gkResponse->response_code() == GKResponseCode::OK) {
             if (gkResponse->payload().size() != 0) {
+                // try to connect to IKeystoreAuthorization AIDL service first.
+                AIBinder* authzAIBinder =
+                        AServiceManager_checkService("android.security.authorization");
+                ::ndk::SpAIBinder authzBinder(authzAIBinder);
+                auto authzService = IKeystoreAuthorization::fromBinder(authzBinder);
+                if (authzService) {
+                    if (gkResponse->payload().size() != sizeof(hw_auth_token_t)) {
+                        LOG(ERROR) << "Incorrect size of AuthToken payload.";
+                        return GK_ERROR;
+                    }
+
+                    const hw_auth_token_t* hwAuthToken =
+                            reinterpret_cast<const hw_auth_token_t*>(gkResponse->payload().data());
+                    HardwareAuthToken authToken;
+
+                    authToken.timestamp.milliSeconds = betoh64(hwAuthToken->timestamp);
+                    authToken.challenge = hwAuthToken->challenge;
+                    authToken.authenticatorId = hwAuthToken->authenticator_id;
+                    authToken.authenticatorType = static_cast<HardwareAuthenticatorType>(
+                            betoh32(hwAuthToken->authenticator_type));
+                    authToken.mac.assign(&hwAuthToken->hmac[0], &hwAuthToken->hmac[32]);
+                    auto result = authzService->addAuthToken(authToken);
+                    if (!result.isOk()) {
+                        LOG(ERROR) << "Failure in sending AuthToken to AuthorizationService.";
+                        return GK_ERROR;
+                    }
+                    AIBinder_decStrong(authzAIBinder);
+                }
                 sp<IServiceManager> sm = defaultServiceManager();
+
                 sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
                 sp<security::keystore::IKeystoreService> service =
                         interface_cast<security::keystore::IKeystoreService>(binder);
@@ -310,9 +341,12 @@
                     if (!binder_result.isOk() ||
                         !keystore::KeyStoreServiceReturnCode(result).isOk()) {
                         LOG(ERROR) << "Failure sending auth token to KeyStore: " << result;
+                        return GK_ERROR;
                     }
                 } else {
-                    LOG(ERROR) << "Cannot deliver auth token. Unable to communicate with Keystore.";
+                    LOG(ERROR) << "Cannot deliver auth token. Unable to communicate with "
+                                  "Keystore.";
+                    return GK_ERROR;
                 }
             }
 
@@ -366,23 +400,23 @@
         }
 
         if (hw_device == NULL) {
-            const char *result = "Device not available";
+            const char* result = "Device not available";
             write(fd, result, strlen(result) + 1);
         } else {
-            const char *result = "OK";
+            const char* result = "OK";
             write(fd, result, strlen(result) + 1);
         }
 
         return OK;
     }
 
-private:
+  private:
     sp<IGatekeeper> hw_device;
 
     bool clear_state_if_needed_done;
     bool is_running_gsi;
 };
-}// namespace android
+}  // namespace android
 
 int main(int argc, char* argv[]) {
     ALOGI("Starting gatekeeperd...");
diff --git a/healthd/Android.bp b/healthd/Android.bp
index b3de9c4..251a45b 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -62,29 +62,6 @@
     srcs: [
         "HealthServiceDefault.cpp",
     ],
-
-    overrides: [
-        "healthd",
-    ]
-}
-
-cc_binary {
-    name: "healthd",
-    defaults: ["android.hardware.health@2.0-service_defaults"],
-
-    init_rc: ["healthd.rc"],
-    srcs: [
-        "HealthServiceHealthd.cpp",
-    ],
-    local_include_dirs: ["include"],
-
-    shared_libs: [
-        "android.hardware.health@1.0",
-    ],
-
-    vintf_fragments: [
-        "manifest_healthd.xml"
-    ],
 }
 
 cc_library_static {
diff --git a/healthd/HealthServiceHealthd.cpp b/healthd/HealthServiceHealthd.cpp
deleted file mode 100644
index 5fd2597..0000000
--- a/healthd/HealthServiceHealthd.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "healthd"
-#include <android-base/logging.h>
-
-#include <android/hardware/health/1.0/IHealth.h>
-#include <android/hardware/health/1.0/types.h>
-#include <hal_conversion.h>
-#include <health2/service.h>
-#include <healthd/healthd.h>
-#include <hidl/HidlTransportSupport.h>
-
-using android::OK;
-using android::NAME_NOT_FOUND;
-using android::hardware::health::V1_0::HealthConfig;
-using android::hardware::health::V1_0::HealthInfo;
-using android::hardware::health::V1_0::Result;
-using android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig;
-using android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
-using android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;
-using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
-
-using IHealthLegacy = android::hardware::health::V1_0::IHealth;
-
-static android::sp<IHealthLegacy> gHealth_1_0;
-
-static int healthd_board_get_energy_counter(int64_t* energy) {
-    if (gHealth_1_0 == nullptr) {
-        return NAME_NOT_FOUND;
-    }
-
-    Result result = Result::NOT_SUPPORTED;
-    gHealth_1_0->energyCounter([energy, &result](Result ret, int64_t energyOut) {
-        result = ret;
-        *energy = energyOut;
-    });
-
-    return result == Result::SUCCESS ? OK : NAME_NOT_FOUND;
-}
-
-void healthd_board_init(struct healthd_config* config) {
-    gHealth_1_0 = IHealthLegacy::getService();
-
-    if (gHealth_1_0 == nullptr) {
-        return;
-    }
-
-    HealthConfig halConfig{};
-    convertToHealthConfig(config, halConfig);
-    gHealth_1_0->init(halConfig, [config](const auto& halConfigOut) {
-        convertFromHealthConfig(halConfigOut, config);
-        // always redirect energy counter queries
-        config->energyCounter = healthd_board_get_energy_counter;
-    });
-    LOG(INFO) << LOG_TAG << ": redirecting calls to 1.0 health HAL";
-}
-
-// TODO(b/68724651): Move this function into healthd_mode_service_2_0_battery_update
-// with logthis returned.
-int healthd_board_battery_update(struct android::BatteryProperties* props) {
-    int logthis = 0;
-
-    if (gHealth_1_0 == nullptr) {
-        return logthis;
-    }
-
-    HealthInfo info;
-    convertToHealthInfo(props, info);
-    gHealth_1_0->update(info, [props, &logthis](int32_t ret, const auto& infoOut) {
-        logthis = ret;
-        convertFromHealthInfo(infoOut, props);
-    });
-
-    return logthis;
-}
-
-int main() {
-    return health_service_main("backup");
-}
diff --git a/healthd/manifest_healthd.xml b/healthd/manifest_healthd.xml
deleted file mode 100644
index 097a7d8..0000000
--- a/healthd/manifest_healthd.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<manifest version="1.0" type="framework">
-    <hal>
-        <name>android.hardware.health</name>
-        <transport>hwbinder</transport>
-        <version>2.0</version>
-        <interface>
-            <name>IHealth</name>
-            <instance>backup</instance>
-        </interface>
-    </hal>
-</manifest>
diff --git a/init/Android.bp b/init/Android.bp
index 19ba21b..cd295cf 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -60,6 +60,7 @@
     "selabel.cpp",
     "selinux.cpp",
     "sigchld_handler.cpp",
+    "snapuserd_transition.cpp",
     "switch_root.cpp",
     "uevent_listener.cpp",
     "ueventd.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index 4c1665b..561d641 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -57,6 +57,8 @@
     reboot_utils.cpp \
     selabel.cpp \
     selinux.cpp \
+    service_utils.cpp \
+    snapuserd_transition.cpp \
     switch_root.cpp \
     uevent_listener.cpp \
     util.cpp \
@@ -75,14 +77,22 @@
    adb_debug.prop \
 
 # Set up the directories that first stage init mounts on.
-LOCAL_POST_INSTALL_CMD := mkdir -p \
-    $(TARGET_RAMDISK_OUT)/debug_ramdisk \
-    $(TARGET_RAMDISK_OUT)/dev \
-    $(TARGET_RAMDISK_OUT)/mnt \
-    $(TARGET_RAMDISK_OUT)/proc \
-    $(TARGET_RAMDISK_OUT)/second_stage_resources \
-    $(TARGET_RAMDISK_OUT)/sys \
-    $(TARGET_RAMDISK_OUT)/metadata \
+
+my_ramdisk_dirs := \
+    debug_ramdisk \
+    dev \
+    metadata \
+    mnt \
+    proc \
+    second_stage_resources \
+    sys \
+
+LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_RAMDISK_OUT)/,$(my_ramdisk_dirs))
+ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE))
+    LOCAL_POST_INSTALL_CMD += $(addprefix $(TARGET_RAMDISK_OUT)/first_stage_ramdisk/,$(my_ramdisk_dirs))
+endif
+
+my_ramdisk_dirs :=
 
 LOCAL_STATIC_LIBRARIES := \
     libc++fs \
diff --git a/init/OWNERS b/init/OWNERS
index babbe4d..9e70e7d 100644
--- a/init/OWNERS
+++ b/init/OWNERS
@@ -1 +1 @@
-tomcherry@google.com
+dvander@google.com
diff --git a/init/README.md b/init/README.md
index ab6a885..67d55e1 100644
--- a/init/README.md
+++ b/init/README.md
@@ -178,6 +178,8 @@
   will reboot into _fatal reboot target_.
   The default value of _fatal crash window mins_ is 4, and default value
   of _fatal reboot target_ is 'bootloader'.
+  For tests, the fatal reboot can be skipped by setting property
+  `init.svc_debug.no_fatal.<service-name>` to `true` for specified critical service.
 
 `disabled`
 > This service will not automatically start with its class.
@@ -451,6 +453,10 @@
   exist. And it will be truncated if dst file is a normal regular file and
   already exists.
 
+`copy_per_line <src> <dst>`
+> Copies a file line by line. Similar to copy, but useful for dst is a sysfs node
+  that doesn't handle multiple lines of data.
+
 `domainname <name>`
 > Set the domain name.
 
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
index 4363f3c..2a76620 100644
--- a/init/README.ueventd.md
+++ b/init/README.ueventd.md
@@ -13,6 +13,16 @@
     uevent_socket_rcvbuf_size 16M
 Sets the uevent socket rcvbuf_size to 16 megabytes.
 
+## Importing configuration files
+--------------------------------
+Ueventd reads /system/etc/ueventd.rc, all other files are imported via the `import` command, which
+takes the format of
+
+    import <path>
+This command parses an ueventd config file, extending the current configuration.  If _path_ is a
+directory, each file in the directory is parsed as a config file. It is not recursive, nested
+directories will not be parsed.  Imported files are parsed after the current file has been parsed.
+
 ## /dev
 ----
 Ueventd listens to the kernel uevent sockets and creates/deletes nodes in `/dev` based on the
@@ -32,7 +42,7 @@
 The permissions can be modified using a ueventd.rc script and a line that beings with `/dev`. These
 lines take the format of
 
-    devname mode uid gid
+    devname mode uid gid [options]
 For example
 
     /dev/null 0666 root root
@@ -70,7 +80,7 @@
 certain files in `/sys` when matching uevents are generated. This is done using a ueventd.rc script
 and a line that begins with `/sys`. These lines take the format of
 
-    nodename attr mode uid gid
+    nodename attr mode uid gid [options]
 For example
 
     /sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system
@@ -78,7 +88,15 @@
 attribute, `cpufreq/scaling_max_freq`, will have its mode set to `0664`, its user to to `system` and
 its group set to `system`.
 
-Note that `*` matches as a wildcard and can be used anywhere in a path.
+## Path matching
+----------------
+The path for a `/dev` or `/sys` entry can contain a `*` anywhere in the path.
+1. If the only `*` appears at the end of the string or if the _options_ parameter is set to
+`no_fnm_pathname`, ueventd matches the entry by `fnmatch(entry_path, incoming_path, 0)`
+2. Otherwise, ueventd matches the entry by `fnmatch(entry_path, incoming_path, FNM_PATHNAME)`
+
+See the [man page for fnmatch](https://www.man7.org/linux/man-pages/man3/fnmatch.3.html) for more
+details.
 
 ## Firmware loading
 ----------------
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
index 8db9793..9c2a7bb 100644
--- a/init/block_dev_initializer.cpp
+++ b/init/block_dev_initializer.cpp
@@ -40,8 +40,8 @@
     return InitMiscDevice("device-mapper");
 }
 
-bool BlockDevInitializer::InitDmUser() {
-    return InitMiscDevice("dm-user");
+bool BlockDevInitializer::InitDmUser(const std::string& name) {
+    return InitMiscDevice("dm-user!" + name);
 }
 
 bool BlockDevInitializer::InitMiscDevice(const std::string& name) {
diff --git a/init/block_dev_initializer.h b/init/block_dev_initializer.h
index b8dd3f1..ec39ce0 100644
--- a/init/block_dev_initializer.h
+++ b/init/block_dev_initializer.h
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#pragma once
+
 #include <memory>
 #include <set>
 #include <string>
@@ -27,7 +29,7 @@
     BlockDevInitializer();
 
     bool InitDeviceMapper();
-    bool InitDmUser();
+    bool InitDmUser(const std::string& name);
     bool InitDevices(std::set<std::string> devices);
     bool InitDmDevice(const std::string& device);
 
diff --git a/init/builtins.cpp b/init/builtins.cpp
index d00d1b1..c44e03e 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -88,6 +88,7 @@
 
 using android::base::Basename;
 using android::base::SetProperty;
+using android::base::Split;
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::unique_fd;
@@ -968,6 +969,23 @@
     return {};
 }
 
+static Result<void> do_copy_per_line(const BuiltinArguments& args) {
+    std::string file_contents;
+    if (!android::base::ReadFileToString(args[1], &file_contents, true)) {
+        return Error() << "Could not read input file '" << args[1] << "'";
+    }
+    auto lines = Split(file_contents, "\n");
+    for (const auto& line : lines) {
+        auto result = WriteFile(args[2], line);
+        if (!result.ok()) {
+            LOG(VERBOSE) << "Could not write to output file '" << args[2] << "' with '" << line
+                         << "' : " << result.error();
+        }
+    }
+
+    return {};
+}
+
 static Result<void> do_chown(const BuiltinArguments& args) {
     auto uid = DecodeUid(args[1]);
     if (!uid.ok()) {
@@ -1214,7 +1232,7 @@
 }
 
 static Result<void> GenerateLinkerConfiguration() {
-    const char* linkerconfig_binary = "/system/bin/linkerconfig";
+    const char* linkerconfig_binary = "/apex/com.android.runtime/bin/linkerconfig";
     const char* linkerconfig_target = "/linkerconfig";
     const char* arguments[] = {linkerconfig_binary, "--target", linkerconfig_target};
 
@@ -1366,6 +1384,7 @@
         {"class_start_post_data",   {1,     1,    {false,  do_class_start_post_data}}},
         {"class_stop",              {1,     1,    {false,  do_class_stop}}},
         {"copy",                    {2,     2,    {true,   do_copy}}},
+        {"copy_per_line",           {2,     2,    {true,   do_copy_per_line}}},
         {"domainname",              {1,     1,    {true,   do_domainname}}},
         {"enable",                  {1,     1,    {false,  do_enable}}},
         {"exec",                    {1,     kMax, {false,  do_exec}}},
diff --git a/init/devices.cpp b/init/devices.cpp
index 5888c06..ce6298a 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -124,8 +124,15 @@
     return true;
 }
 
-Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
-    : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
+Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid,
+                         bool no_fnm_pathname)
+    : name_(name),
+      perm_(perm),
+      uid_(uid),
+      gid_(gid),
+      prefix_(false),
+      wildcard_(false),
+      no_fnm_pathname_(no_fnm_pathname) {
     // Set 'prefix_' or 'wildcard_' based on the below cases:
     //
     // 1) No '*' in 'name' -> Neither are set and Match() checks a given path for strict
@@ -136,7 +143,6 @@
     //
     // 3) '*' appears elsewhere -> 'wildcard_' is set to true and Match() uses fnmatch()
     //    with FNM_PATHNAME to compare 'name' to a given path.
-
     auto wildcard_position = name_.find('*');
     if (wildcard_position != std::string::npos) {
         if (wildcard_position == name_.length() - 1) {
@@ -150,7 +156,8 @@
 
 bool Permissions::Match(const std::string& path) const {
     if (prefix_) return StartsWith(path, name_);
-    if (wildcard_) return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
+    if (wildcard_)
+        return fnmatch(name_.c_str(), path.c_str(), no_fnm_pathname_ ? 0 : FNM_PATHNAME) == 0;
     return path == name_;
 }
 
@@ -461,9 +468,10 @@
 }
 
 void DeviceHandler::HandleUevent(const Uevent& uevent) {
-    if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
-        FixupSysPermissions(uevent.path, uevent.subsystem);
-    }
+  if (uevent.action == "add" || uevent.action == "change" ||
+      uevent.action == "bind" || uevent.action == "online") {
+    FixupSysPermissions(uevent.path, uevent.subsystem);
+  }
 
     // if it's not a /dev device, nothing to do
     if (uevent.major < 0 || uevent.minor < 0) return;
diff --git a/init/devices.h b/init/devices.h
index 05d64da..d70d746 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -38,7 +38,7 @@
   public:
     friend void TestPermissions(const Permissions& expected, const Permissions& test);
 
-    Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid);
+    Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid, bool no_fnm_pathname);
 
     bool Match(const std::string& path) const;
 
@@ -56,6 +56,7 @@
     gid_t gid_;
     bool prefix_;
     bool wildcard_;
+    bool no_fnm_pathname_;
 };
 
 class SysfsPermissions : public Permissions {
@@ -63,8 +64,8 @@
     friend void TestSysfsPermissions(const SysfsPermissions& expected, const SysfsPermissions& test);
 
     SysfsPermissions(const std::string& name, const std::string& attribute, mode_t perm, uid_t uid,
-                     gid_t gid)
-        : Permissions(name, perm, uid, gid), attribute_(attribute) {}
+                     gid_t gid, bool no_fnm_pathname)
+        : Permissions(name, perm, uid, gid, no_fnm_pathname), attribute_(attribute) {}
 
     bool MatchWithSubsystem(const std::string& path, const std::string& subsystem) const;
     void SetPermissions(const std::string& path) const;
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index c408bc1..e7bac68 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -221,7 +221,7 @@
 TEST(device_handler, DevPermissionsMatchNormal) {
     // Basic from ueventd.rc
     // /dev/null                 0666   root       root
-    Permissions permissions("/dev/null", 0666, 0, 0);
+    Permissions permissions("/dev/null", 0666, 0, 0, false);
     EXPECT_TRUE(permissions.Match("/dev/null"));
     EXPECT_FALSE(permissions.Match("/dev/nullsuffix"));
     EXPECT_FALSE(permissions.Match("/dev/nul"));
@@ -233,7 +233,7 @@
 TEST(device_handler, DevPermissionsMatchPrefix) {
     // Prefix from ueventd.rc
     // /dev/dri/*                0666   root       graphics
-    Permissions permissions("/dev/dri/*", 0666, 0, 1000);
+    Permissions permissions("/dev/dri/*", 0666, 0, 1000, false);
     EXPECT_TRUE(permissions.Match("/dev/dri/some_dri_device"));
     EXPECT_TRUE(permissions.Match("/dev/dri/some_other_dri_device"));
     EXPECT_TRUE(permissions.Match("/dev/dri/"));
@@ -246,7 +246,7 @@
 TEST(device_handler, DevPermissionsMatchWildcard) {
     // Wildcard example
     // /dev/device*name                0666   root       graphics
-    Permissions permissions("/dev/device*name", 0666, 0, 1000);
+    Permissions permissions("/dev/device*name", 0666, 0, 1000, false);
     EXPECT_TRUE(permissions.Match("/dev/devicename"));
     EXPECT_TRUE(permissions.Match("/dev/device123name"));
     EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
@@ -260,13 +260,31 @@
 TEST(device_handler, DevPermissionsMatchWildcardPrefix) {
     // Wildcard+Prefix example
     // /dev/device*name*                0666   root       graphics
-    Permissions permissions("/dev/device*name*", 0666, 0, 1000);
+    Permissions permissions("/dev/device*name*", 0666, 0, 1000, false);
     EXPECT_TRUE(permissions.Match("/dev/devicename"));
     EXPECT_TRUE(permissions.Match("/dev/device123name"));
     EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
     EXPECT_TRUE(permissions.Match("/dev/device123namesomething"));
     // FNM_PATHNAME doesn't match '/' with *
     EXPECT_FALSE(permissions.Match("/dev/device123name/something"));
+    EXPECT_FALSE(permissions.Match("/dev/device/1/2/3name/something"));
+    EXPECT_FALSE(permissions.Match("/dev/deviceame"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(device_handler, DevPermissionsMatchWildcardPrefix_NoFnmPathName) {
+    // Wildcard+Prefix example with no_fnm_pathname
+    // /dev/device*name*                0666   root       graphics
+    Permissions permissions("/dev/device*name*", 0666, 0, 1000, true);
+    EXPECT_TRUE(permissions.Match("/dev/devicename"));
+    EXPECT_TRUE(permissions.Match("/dev/device123name"));
+    EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
+    EXPECT_TRUE(permissions.Match("/dev/device123namesomething"));
+    // With NoFnmPathName, the below matches, unlike DevPermissionsMatchWildcardPrefix.
+    EXPECT_TRUE(permissions.Match("/dev/device123name/something"));
+    EXPECT_TRUE(permissions.Match("/dev/device/1/2/3name/something"));
     EXPECT_FALSE(permissions.Match("/dev/deviceame"));
     EXPECT_EQ(0666U, permissions.perm());
     EXPECT_EQ(0U, permissions.uid());
@@ -275,7 +293,8 @@
 
 TEST(device_handler, SysfsPermissionsMatchWithSubsystemNormal) {
     // /sys/devices/virtual/input/input*   enable      0660  root   input
-    SysfsPermissions permissions("/sys/devices/virtual/input/input*", "enable", 0660, 0, 1001);
+    SysfsPermissions permissions("/sys/devices/virtual/input/input*", "enable", 0660, 0, 1001,
+                                 false);
     EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/input0", "input"));
     EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/not_input0", "input"));
     EXPECT_EQ(0660U, permissions.perm());
@@ -285,7 +304,7 @@
 
 TEST(device_handler, SysfsPermissionsMatchWithSubsystemClass) {
     // /sys/class/input/event*   enable      0660  root   input
-    SysfsPermissions permissions("/sys/class/input/event*", "enable", 0660, 0, 1001);
+    SysfsPermissions permissions("/sys/class/input/event*", "enable", 0660, 0, 1001, false);
     EXPECT_TRUE(permissions.MatchWithSubsystem(
         "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0", "input"));
     EXPECT_FALSE(permissions.MatchWithSubsystem(
@@ -299,7 +318,7 @@
 
 TEST(device_handler, SysfsPermissionsMatchWithSubsystemBus) {
     // /sys/bus/i2c/devices/i2c-*   enable      0660  root   input
-    SysfsPermissions permissions("/sys/bus/i2c/devices/i2c-*", "enable", 0660, 0, 1001);
+    SysfsPermissions permissions("/sys/bus/i2c/devices/i2c-*", "enable", 0660, 0, 1001, false);
     EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "i2c"));
     EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/not-i2c", "i2c"));
     EXPECT_FALSE(
diff --git a/init/firmware_handler_test.cpp b/init/firmware_handler_test.cpp
index 7bb603c..5124a6f 100644
--- a/init/firmware_handler_test.cpp
+++ b/init/firmware_handler_test.cpp
@@ -79,6 +79,8 @@
 }
 
 int HandleAbort(int argc, char** argv) {
+    // Since this is an expected failure, disable debuggerd to not generate a tombstone.
+    signal(SIGABRT, SIG_DFL);
     abort();
     return 0;
 }
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 91aaffd..6954c03 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -42,6 +42,7 @@
 #include "first_stage_mount.h"
 #include "reboot_utils.h"
 #include "second_stage_resources.h"
+#include "snapuserd_transition.h"
 #include "switch_root.h"
 #include "util.h"
 
@@ -90,6 +91,12 @@
                     }
                 }
             }
+        } else if (de->d_type == DT_REG) {
+            // Do not free snapuserd if we will need the ramdisk copy during the
+            // selinux transition.
+            if (de->d_name == "snapuserd"s && IsFirstStageSnapuserdRunning()) {
+                continue;
+            }
         }
         unlinkat(dfd, de->d_name, is_dir ? AT_REMOVEDIR : 0);
     }
@@ -99,34 +106,6 @@
     return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
 }
 
-// Move e2fsck before switching root, so that it is available at the same path
-// after switching root.
-void PrepareSwitchRoot() {
-    constexpr const char* src = "/system/bin/e2fsck";
-    constexpr const char* dst = "/first_stage_ramdisk/system/bin/e2fsck";
-
-    if (access(dst, X_OK) == 0) {
-        LOG(INFO) << dst << " already exists and it can be executed";
-        return;
-    }
-
-    if (access(src, F_OK) != 0) {
-        PLOG(INFO) << "Not moving " << src << " because it cannot be accessed";
-        return;
-    }
-
-    auto dst_dir = android::base::Dirname(dst);
-    std::error_code ec;
-    if (!fs::create_directories(dst_dir, ec)) {
-        LOG(FATAL) << "Cannot create " << dst_dir << ": " << ec.message();
-    }
-    if (rename(src, dst) != 0) {
-        PLOG(FATAL) << "Cannot move " << src << " to " << dst
-                    << ". Either install e2fsck.ramdisk so that it is at the correct place (" << dst
-                    << "), or make ramdisk writable";
-    }
-}
-
 }  // namespace
 
 std::string GetModuleLoadList(bool recovery, const std::string& dir_path) {
@@ -315,7 +294,7 @@
         std::string dest = GetRamdiskPropForSecondStage();
         std::string dir = android::base::Dirname(dest);
         std::error_code ec;
-        if (!fs::create_directories(dir, ec)) {
+        if (!fs::create_directories(dir, ec) && !!ec) {
             LOG(FATAL) << "Can't mkdir " << dir << ": " << ec.message();
         }
         if (!fs::copy_file(kBootImageRamdiskProp, dest, ec)) {
@@ -327,7 +306,6 @@
 
     if (ForceNormalBoot(cmdline)) {
         mkdir("/first_stage_ramdisk", 0755);
-        PrepareSwitchRoot();
         // SwitchRoot() must be called with a mount point as the target, so we bind mount the
         // target directory to itself here.
         if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index b7d50cf..7c46281 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -44,6 +44,7 @@
 
 #include "block_dev_initializer.h"
 #include "devices.h"
+#include "snapuserd_transition.h"
 #include "switch_root.h"
 #include "uevent.h"
 #include "uevent_listener.h"
@@ -87,6 +88,7 @@
   protected:
     bool InitRequiredDevices(std::set<std::string> devices);
     bool CreateLogicalPartitions();
+    bool CreateSnapshotPartitions(android::snapshot::SnapshotManager* sm);
     bool MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
                         Fstab::iterator* end = nullptr);
 
@@ -109,6 +111,7 @@
 
     bool need_dm_verity_;
     bool dsu_not_on_userdata_ = false;
+    bool use_snapuserd_ = false;
 
     Fstab fstab_;
     // The super path is only set after InitDevices, and is invalid before.
@@ -338,12 +341,7 @@
             return false;
         }
         if (sm->NeedSnapshotsInFirstStageMount()) {
-            // When COW images are present for snapshots, they are stored on
-            // the data partition.
-            if (!InitRequiredDevices({"userdata"})) {
-                return false;
-            }
-            return sm->CreateLogicalAndSnapshotPartitions(super_path_);
+            return CreateSnapshotPartitions(sm.get());
         }
     }
 
@@ -358,6 +356,37 @@
     return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path_);
 }
 
+bool FirstStageMount::CreateSnapshotPartitions(SnapshotManager* sm) {
+    // When COW images are present for snapshots, they are stored on
+    // the data partition.
+    if (!InitRequiredDevices({"userdata"})) {
+        return false;
+    }
+
+    use_snapuserd_ = sm->IsSnapuserdRequired();
+    if (use_snapuserd_) {
+        LaunchFirstStageSnapuserd();
+    }
+
+    sm->SetUeventRegenCallback([this](const std::string& device) -> bool {
+        if (android::base::StartsWith(device, "/dev/block/dm-")) {
+            return block_dev_init_.InitDmDevice(device);
+        }
+        if (android::base::StartsWith(device, "/dev/dm-user/")) {
+            return block_dev_init_.InitDmUser(android::base::Basename(device));
+        }
+        return block_dev_init_.InitDevices({device});
+    });
+    if (!sm->CreateLogicalAndSnapshotPartitions(super_path_)) {
+        return false;
+    }
+
+    if (use_snapuserd_) {
+        CleanupSnapuserdSocket();
+    }
+    return true;
+}
+
 bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
                                      Fstab::iterator* end) {
     // Sets end to begin + 1, so we can just return on failure below.
@@ -457,6 +486,10 @@
 
     if (system_partition == fstab_.end()) return true;
 
+    if (use_snapuserd_) {
+        SaveRamdiskPathToSnapuserd();
+    }
+
     if (MountPartition(system_partition, false /* erase_same_mounts */)) {
         if (dsu_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
             LOG(ERROR) << "check_most_at_once forbidden on external media";
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index ef9a451..db127d3 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -25,6 +25,8 @@
 #include <fstream>
 #include <iostream>
 #include <iterator>
+#include <map>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -51,6 +53,7 @@
 
 using namespace std::literals;
 
+using android::base::EndsWith;
 using android::base::ParseInt;
 using android::base::ReadFileToString;
 using android::base::Split;
@@ -61,6 +64,10 @@
 
 static std::vector<std::string> passwd_files;
 
+// NOTE: Keep this in sync with the order used by init.cpp LoadBootScripts()
+static const std::vector<std::string> partition_search_order =
+        std::vector<std::string>({"system", "system_ext", "odm", "vendor", "product"});
+
 static std::vector<std::pair<std::string, int>> GetVendorPasswd(const std::string& passwd_file) {
     std::string passwd;
     if (!ReadFileToString(passwd_file, &passwd)) {
@@ -148,13 +155,24 @@
 #include "generated_stub_builtin_function_map.h"
 
 void PrintUsage() {
-    std::cout << "usage: host_init_verifier [options] <init rc file>\n"
-                 "\n"
-                 "Tests an init script for correctness\n"
-                 "\n"
-                 "-p FILE\tSearch this passwd file for users and groups\n"
-                 "--property_contexts=FILE\t Use this file for property_contexts\n"
-              << std::endl;
+    fprintf(stdout, R"(usage: host_init_verifier [options]
+
+Tests init script(s) for correctness.
+
+Generic options:
+  -p FILE                     Search this passwd file for users and groups.
+  --property_contexts=FILE    Use this file for property_contexts.
+
+Single script mode options:
+  [init rc file]              Positional argument; test this init script.
+
+Multiple script mode options:
+  --out_system=DIR            Path to the output product directory for the system partition.
+  --out_system_ext=DIR        Path to the output product directory for the system_ext partition.
+  --out_odm=DIR               Path to the output product directory for the odm partition.
+  --out_vendor=DIR            Path to the output product directory for the vendor partition.
+  --out_product=DIR           Path to the output product directory for the product partition.
+)");
 }
 
 Result<InterfaceInheritanceHierarchyMap> ReadInterfaceInheritanceHierarchy() {
@@ -203,12 +221,18 @@
     android::base::SetMinimumLogSeverity(android::base::ERROR);
 
     auto property_infos = std::vector<PropertyInfoEntry>();
+    std::map<std::string, std::string> partition_map;
 
     while (true) {
         static const char kPropertyContexts[] = "property-contexts=";
         static const struct option long_options[] = {
                 {"help", no_argument, nullptr, 'h'},
                 {kPropertyContexts, required_argument, nullptr, 0},
+                {"out_system", required_argument, nullptr, 0},
+                {"out_system_ext", required_argument, nullptr, 0},
+                {"out_odm", required_argument, nullptr, 0},
+                {"out_vendor", required_argument, nullptr, 0},
+                {"out_product", required_argument, nullptr, 0},
                 {nullptr, 0, nullptr, 0},
         };
 
@@ -224,6 +248,16 @@
                 if (long_options[option_index].name == kPropertyContexts) {
                     HandlePropertyContexts(optarg, &property_infos);
                 }
+                for (const auto& p : partition_search_order) {
+                    if (long_options[option_index].name == "out_" + p) {
+                        if (partition_map.find(p) != partition_map.end()) {
+                            PrintUsage();
+                            return EXIT_FAILURE;
+                        }
+                        partition_map[p] =
+                                EndsWith(optarg, "/") ? optarg : std::string(optarg) + "/";
+                    }
+                }
                 break;
             case 'h':
                 PrintUsage();
@@ -240,7 +274,9 @@
     argc -= optind;
     argv += optind;
 
-    if (argc != 1) {
+    // If provided, use the partition map to check multiple init rc files.
+    // Otherwise, check a single init rc file.
+    if ((!partition_map.empty() && argc != 0) || (partition_map.empty() && argc != 1)) {
         PrintUsage();
         return EXIT_FAILURE;
     }
@@ -262,24 +298,42 @@
 
     property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_contexts.c_str());
 
+    if (!partition_map.empty()) {
+        std::vector<std::string> vendor_prefixes;
+        for (const auto& partition : {"vendor", "odm"}) {
+            if (partition_map.find(partition) != partition_map.end()) {
+                vendor_prefixes.push_back(partition_map.at(partition));
+            }
+        }
+        InitializeHostSubcontext(vendor_prefixes);
+    }
+
     const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
     Action::set_function_map(&function_map);
     ActionManager& am = ActionManager::GetInstance();
     ServiceList& sl = ServiceList::GetInstance();
     Parser parser;
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(
-                                               &sl, nullptr, *interface_inheritance_hierarchy_map));
-    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+    parser.AddSectionParser("service",
+                            std::make_unique<ServiceParser>(&sl, GetSubcontext(),
+                                                            *interface_inheritance_hierarchy_map));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, GetSubcontext()));
     parser.AddSectionParser("import", std::make_unique<HostImportParser>());
 
-    if (!parser.ParseConfigFileInsecure(*argv)) {
-        LOG(ERROR) << "Failed to open init rc script '" << *argv << "'";
-        return EXIT_FAILURE;
+    if (!partition_map.empty()) {
+        for (const auto& p : partition_search_order) {
+            if (partition_map.find(p) != partition_map.end()) {
+                parser.ParseConfig(partition_map.at(p) + "etc/init");
+            }
+        }
+    } else {
+        if (!parser.ParseConfigFileInsecure(*argv)) {
+            LOG(ERROR) << "Failed to open init rc script '" << *argv << "'";
+            return EXIT_FAILURE;
+        }
     }
     size_t failures = parser.parse_error_count() + am.CheckAllCommands() + sl.CheckAllCommands();
     if (failures > 0) {
-        LOG(ERROR) << "Failed to parse init script '" << *argv << "' with " << failures
-                   << " errors";
+        LOG(ERROR) << "Failed to parse init scripts with " << failures << " error(s).";
         return EXIT_FAILURE;
     }
     return EXIT_SUCCESS;
diff --git a/init/init.cpp b/init/init.cpp
index c6f2066..ca2d5da 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -79,6 +79,7 @@
 #include "service.h"
 #include "service_parser.h"
 #include "sigchld_handler.h"
+#include "snapuserd_transition.h"
 #include "subcontext.h"
 #include "system/core/init/property_service.pb.h"
 #include "util.h"
@@ -266,12 +267,10 @@
     if (shutdown_state.do_shutdown()) {
         LOG(ERROR) << "sys.powerctl set while a previous shutdown command has not been handled";
         UnwindMainThreadStack();
-        DumpShutdownDebugInformation();
     }
     if (IsShuttingDown()) {
         LOG(ERROR) << "sys.powerctl set while init is already shutting down";
         UnwindMainThreadStack();
-        DumpShutdownDebugInformation();
     }
 }
 
@@ -743,10 +742,15 @@
         return {};
     }
     svc->Start();
+    svc->SetShutdownCritical();
 
-    if (!sm->PerformSecondStageTransition()) {
+    if (!sm->PerformSecondStageInitTransition()) {
         LOG(FATAL) << "Failed to transition snapuserd to second-stage";
     }
+
+    if (auto pid = GetSnapuserdFirstStagePid()) {
+        KillFirstStageSnapuserd(pid.value());
+    }
     return {};
 }
 
@@ -760,7 +764,7 @@
     trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
 
     SetStdioToDevNull(argv);
-    InitSecondStageLogging(argv);
+    InitKernelLogging(argv);
     LOG(INFO) << "init second stage started!";
 
     // Update $PATH in the case the second stage init is newer than first stage init, where it is
@@ -874,14 +878,29 @@
     auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0";
     SetProperty(gsi::kGsiInstalledProp, is_installed);
 
+    /*
+     * For debug builds of S launching devices, init mounts debugfs for
+     * enabling vendor debug data collection setup at boot time. Init will unmount it on
+     * boot-complete after vendor code has performed the required initializations
+     * during boot. Dumpstate will then mount debugfs in order to read data
+     * from the same using the dumpstate HAL during bugreport creation.
+     * Dumpstate will also unmount debugfs after bugreport creation.
+     * first_api_level comparison is done here instead
+     * of init.rc since init.rc parser does not support >/< operators.
+     */
+    auto api_level = android::base::GetIntProperty("ro.product.first_api_level", 0);
+    bool is_debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+    auto mount_debugfs = (is_debuggable && (api_level >= 31)) ? "1" : "0";
+    SetProperty("init.mount_debugfs", mount_debugfs);
+
     am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
-    am.QueueBuiltinAction(TransitionSnapuserdAction, "TransitionSnapuserd");
     am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
     am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
     am.QueueEventTrigger("early-init");
 
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
     am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
+    am.QueueBuiltinAction(TransitionSnapuserdAction, "TransitionSnapuserd");
     // ... so that we can start queuing up actions that require stuff from /dev.
     am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
     am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
diff --git a/init/init_test.cpp b/init/init_test.cpp
index fa65740..8550ec8 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -17,6 +17,7 @@
 #include <functional>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <gtest/gtest.h>
 
@@ -268,6 +269,17 @@
     ASSERT_EQ(1u, parser.parse_error_count());
 }
 
+class TestCaseLogger : public ::testing::EmptyTestEventListener {
+    void OnTestStart(const ::testing::TestInfo& test_info) override {
+#ifdef __ANDROID__
+        LOG(INFO) << "===== " << test_info.test_suite_name() << "::" << test_info.name() << " ("
+                  << test_info.file() << ":" << test_info.line() << ")";
+#else
+        UNUSED(test_info);
+#endif
+    }
+};
+
 }  // namespace init
 }  // namespace android
 
@@ -284,5 +296,6 @@
     }
 
     testing::InitGoogleTest(&argc, argv);
+    testing::UnitTest::GetInstance()->listeners().Append(new android::init::TestCaseLogger());
     return RUN_ALL_TESTS();
 }
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 59cc140..ec48cde 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -115,22 +115,29 @@
         return {};
     }
     dirent* entry;
+    std::vector<std::string> entries;
+
     while ((entry = readdir(dir.get())) != nullptr) {
         if (entry->d_name[0] == '.') continue;
         if (entry->d_type == DT_DIR) {
-            const std::string apex_path = from_dir + "/" + entry->d_name;
-            const auto apex_manifest = GetApexManifest(apex_path);
-            if (!apex_manifest.ok()) {
-                LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_manifest.error();
-                continue;
-            }
-            const std::string mount_path = to_dir + "/" + apex_manifest->name();
-            if (auto result = MountDir(apex_path, mount_path); !result.ok()) {
-                return result;
-            }
-            on_activate(apex_path, *apex_manifest);
+            entries.push_back(entry->d_name);
         }
     }
+
+    std::sort(entries.begin(), entries.end());
+    for (const auto& name : entries) {
+        const std::string apex_path = from_dir + "/" + name;
+        const auto apex_manifest = GetApexManifest(apex_path);
+        if (!apex_manifest.ok()) {
+            LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_manifest.error();
+            continue;
+        }
+        const std::string mount_path = to_dir + "/" + apex_manifest->name();
+        if (auto result = MountDir(apex_path, mount_path); !result.ok()) {
+            return result;
+        }
+        on_activate(apex_path, *apex_manifest);
+    }
     return {};
 }
 
diff --git a/init/property_service.cpp b/init/property_service.cpp
index e71c386..ce67386 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -890,6 +890,69 @@
     }
 }
 
+// If the ro.product.cpu.abilist* properties have not been explicitly
+// set, derive them from ro.${partition}.product.cpu.abilist* properties.
+static void property_initialize_ro_cpu_abilist() {
+    // From high to low priority.
+    const char* kAbilistSources[] = {
+            "product",
+            "odm",
+            "vendor",
+            "system",
+    };
+    const std::string EMPTY = "";
+    const char* kAbilistProp = "ro.product.cpu.abilist";
+    const char* kAbilist32Prop = "ro.product.cpu.abilist32";
+    const char* kAbilist64Prop = "ro.product.cpu.abilist64";
+
+    // If the properties are defined explicitly, just use them.
+    if (GetProperty(kAbilistProp, EMPTY) != EMPTY) {
+        return;
+    }
+
+    // Find the first source defining these properties by order.
+    std::string abilist32_prop_val;
+    std::string abilist64_prop_val;
+    for (const auto& source : kAbilistSources) {
+        const auto abilist32_prop = std::string("ro.") + source + ".product.cpu.abilist32";
+        const auto abilist64_prop = std::string("ro.") + source + ".product.cpu.abilist64";
+        abilist32_prop_val = GetProperty(abilist32_prop, EMPTY);
+        abilist64_prop_val = GetProperty(abilist64_prop, EMPTY);
+        // The properties could be empty on 32-bit-only or 64-bit-only devices,
+        // but we cannot identify a property is empty or undefined by GetProperty().
+        // So, we assume both of these 2 properties are empty as undefined.
+        if (abilist32_prop_val != EMPTY || abilist64_prop_val != EMPTY) {
+            break;
+        }
+    }
+
+    // Merge ABI lists for ro.product.cpu.abilist
+    auto abilist_prop_val = abilist64_prop_val;
+    if (abilist32_prop_val != EMPTY) {
+        if (abilist_prop_val != EMPTY) {
+            abilist_prop_val += ",";
+        }
+        abilist_prop_val += abilist32_prop_val;
+    }
+
+    // Set these properties
+    const std::pair<const char*, const std::string&> set_prop_list[] = {
+            {kAbilistProp, abilist_prop_val},
+            {kAbilist32Prop, abilist32_prop_val},
+            {kAbilist64Prop, abilist64_prop_val},
+    };
+    for (const auto& [prop, prop_val] : set_prop_list) {
+        LOG(INFO) << "Setting property '" << prop << "' to '" << prop_val << "'";
+
+        std::string error;
+        uint32_t res = PropertySet(prop, prop_val, &error);
+        if (res != PROP_SUCCESS) {
+            LOG(ERROR) << "Error setting property '" << prop << "': err=" << res << " (" << error
+                       << ")";
+        }
+    }
+}
+
 void PropertyLoadBootDefaults() {
     // We read the properties and their values into a map, in order to always allow properties
     // loaded in the later property files to override the properties in loaded in the earlier
@@ -957,8 +1020,6 @@
     load_properties_from_partition("odm", /* support_legacy_path_until */ 28);
     load_properties_from_partition("product", /* support_legacy_path_until */ 30);
 
-    load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
-
     if (access(kDebugRamdiskProp, R_OK) == 0) {
         LOG(INFO) << "Loading " << kDebugRamdiskProp;
         load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
@@ -974,6 +1035,7 @@
 
     property_initialize_ro_product_props();
     property_derive_build_fingerprint();
+    property_initialize_ro_cpu_abilist();
 
     update_sys_usb_config();
 }
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 49baf9e..e3aaa38 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -655,6 +655,7 @@
 
         if (do_shutdown_animation) {
             SetProperty("service.bootanim.exit", "0");
+            SetProperty("service.bootanim.progress", "0");
             // Could be in the middle of animation. Stop and start so that it can pick
             // up the right mode.
             boot_anim->Stop();
@@ -853,7 +854,7 @@
         sub_reason = "apex";
         return result;
     }
-    if (!SwitchToMountNamespaceIfNeeded(NS_BOOTSTRAP)) {
+    if (!SwitchToMountNamespaceIfNeeded(NS_BOOTSTRAP).ok()) {
         sub_reason = "ns_switch";
         return Error() << "Failed to switch to bootstrap namespace";
     }
diff --git a/init/selinux.cpp b/init/selinux.cpp
index f03ca6b..0336936 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -45,7 +45,7 @@
 // 2) If these hashes do not match, then either /system or /system_ext or /product (or some of them)
 //    have been updated out of sync with /vendor (or /odm if it is present) and the init needs to
 //    compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it is used by the
-//    LoadSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
+//    OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
 //    That function contains even more documentation with the specific implementation details of how
 //    the SEPolicy is compiled if needed.
 
@@ -74,6 +74,7 @@
 #include "block_dev_initializer.h"
 #include "debug_ramdisk.h"
 #include "reboot_utils.h"
+#include "snapuserd_transition.h"
 #include "util.h"
 
 using namespace std::string_literals;
@@ -298,7 +299,12 @@
     return access(plat_policy_cil_file, R_OK) != -1;
 }
 
-bool LoadSplitPolicy() {
+struct PolicyFile {
+    unique_fd fd;
+    std::string path;
+};
+
+bool OpenSplitPolicy(PolicyFile* policy_file) {
     // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
     // * platform -- policy needed due to logic contained in the system image,
     // * non-platform -- policy needed due to logic contained in the vendor image,
@@ -325,10 +331,8 @@
     if (!use_userdebug_policy && FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
         unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
         if (fd != -1) {
-            if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
-                LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
-                return false;
-            }
+            policy_file->fd = std::move(fd);
+            policy_file->path = std::move(precompiled_sepolicy_file);
             return true;
         }
     }
@@ -446,34 +450,39 @@
     }
     unlink(compiled_sepolicy);
 
-    LOG(INFO) << "Loading compiled SELinux policy";
-    if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
-        LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
-        return false;
-    }
-
+    policy_file->fd = std::move(compiled_sepolicy_fd);
+    policy_file->path = compiled_sepolicy;
     return true;
 }
 
-bool LoadMonolithicPolicy() {
-    LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
-    if (selinux_android_load_policy() < 0) {
-        PLOG(ERROR) << "Failed to load monolithic SELinux policy";
+bool OpenMonolithicPolicy(PolicyFile* policy_file) {
+    static constexpr char kSepolicyFile[] = "/sepolicy";
+
+    LOG(VERBOSE) << "Opening SELinux policy from monolithic file";
+    policy_file->fd.reset(open(kSepolicyFile, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+    if (policy_file->fd < 0) {
+        PLOG(ERROR) << "Failed to open monolithic SELinux policy";
         return false;
     }
+    policy_file->path = kSepolicyFile;
     return true;
 }
 
-bool LoadPolicy() {
-    return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
-}
+void ReadPolicy(std::string* policy) {
+    PolicyFile policy_file;
 
-void SelinuxInitialize() {
-    LOG(INFO) << "Loading SELinux policy";
-    if (!LoadPolicy()) {
-        LOG(FATAL) << "Unable to load SELinux policy";
+    bool ok = IsSplitPolicyDevice() ? OpenSplitPolicy(&policy_file)
+                                    : OpenMonolithicPolicy(&policy_file);
+    if (!ok) {
+        LOG(FATAL) << "Unable to open SELinux policy";
     }
 
+    if (!android::base::ReadFdToString(policy_file.fd, policy)) {
+        PLOG(FATAL) << "Failed to read policy file: " << policy_file.path;
+    }
+}
+
+void SelinuxSetEnforcement() {
     bool kernel_enforcing = (security_getenforce() == 1);
     bool is_enforcing = IsEnforcing();
     if (kernel_enforcing != is_enforcing) {
@@ -670,6 +679,30 @@
     }
 }
 
+static void LoadSelinuxPolicy(std::string& policy) {
+    LOG(INFO) << "Loading SELinux policy";
+
+    set_selinuxmnt("/sys/fs/selinux");
+    if (security_load_policy(policy.data(), policy.size()) < 0) {
+        PLOG(FATAL) << "SELinux:  Could not load policy";
+    }
+}
+
+// The SELinux setup process is carefully orchestrated around snapuserd. Policy
+// must be loaded off dynamic partitions, and during an OTA, those partitions
+// cannot be read without snapuserd. But, with kernel-privileged snapuserd
+// running, loading the policy will immediately trigger audits.
+//
+// We use a five-step process to address this:
+//  (1) Read the policy into a string, with snapuserd running.
+//  (2) Rewrite the snapshot device-mapper tables, to generate new dm-user
+//      devices and to flush I/O.
+//  (3) Kill snapuserd, which no longer has any dm-user devices to attach to.
+//  (4) Load the sepolicy and issue critical restorecons in /dev, carefully
+//      avoiding anything that would read from /system.
+//  (5) Re-launch snapuserd and attach it to the dm-user devices from step (2).
+//
+// After this sequence, it is safe to enable enforcing mode and continue booting.
 int SetupSelinux(char** argv) {
     SetStdioToDevNull(argv);
     InitKernelLogging(argv);
@@ -682,9 +715,31 @@
 
     MountMissingSystemPartitions();
 
-    // Set up SELinux, loading the SELinux policy.
     SelinuxSetupKernelLogging();
-    SelinuxInitialize();
+
+    LOG(INFO) << "Opening SELinux policy";
+
+    // Read the policy before potentially killing snapuserd.
+    std::string policy;
+    ReadPolicy(&policy);
+
+    auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
+    if (snapuserd_helper) {
+        // Kill the old snapused to avoid audit messages. After this we cannot
+        // read from /system (or other dynamic partitions) until we call
+        // FinishTransition().
+        snapuserd_helper->StartTransition();
+    }
+
+    LoadSelinuxPolicy(policy);
+
+    if (snapuserd_helper) {
+        // Before enforcing, finish the pending snapuserd transition.
+        snapuserd_helper->FinishTransition();
+        snapuserd_helper = nullptr;
+    }
+
+    SelinuxSetEnforcement();
 
     // We're in the kernel domain and want to transition to the init domain.  File systems that
     // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
diff --git a/init/service.cpp b/init/service.cpp
index 7b98392..f6ce094 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -52,6 +52,7 @@
 #endif
 
 using android::base::boot_clock;
+using android::base::GetBoolProperty;
 using android::base::GetProperty;
 using android::base::Join;
 using android::base::make_scope_guard;
@@ -72,12 +73,12 @@
     if (getcon(&raw_con) == -1) {
         return Error() << "Could not get security context";
     }
-    std::unique_ptr<char> mycon(raw_con);
+    std::unique_ptr<char, decltype(&freecon)> mycon(raw_con, freecon);
 
     if (getfilecon(service_path.c_str(), &raw_filecon) == -1) {
         return Error() << "Could not get file context";
     }
-    std::unique_ptr<char> filecon(raw_filecon);
+    std::unique_ptr<char, decltype(&freecon)> filecon(raw_filecon, freecon);
 
     char* new_con = nullptr;
     int rc = security_compute_create(mycon.get(), filecon.get(),
@@ -154,6 +155,7 @@
                  .priority = 0},
       namespaces_{.flags = namespace_flags},
       seclabel_(seclabel),
+      subcontext_(subcontext_for_restart_commands),
       onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
                  "onrestart", {}),
       oom_score_adjust_(DEFAULT_OOM_SCORE_ADJUST),
@@ -317,17 +319,19 @@
     // reboot into bootloader or set crashing property
     boot_clock::time_point now = boot_clock::now();
     if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART)) {
-        bool boot_completed = android::base::GetBoolProperty("sys.boot_completed", false);
+        bool boot_completed = GetBoolProperty("sys.boot_completed", false);
         if (now < time_crashed_ + fatal_crash_window_ || !boot_completed) {
             if (++crash_count_ > 4) {
                 auto exit_reason = boot_completed ?
                     "in " + std::to_string(fatal_crash_window_.count()) + " minutes" :
                     "before boot completed";
                 if (flags_ & SVC_CRITICAL) {
-                    // Aborts into `fatal_reboot_target_'.
-                    SetFatalRebootTarget(fatal_reboot_target_);
-                    LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
-                               << exit_reason;
+                    if (!GetBoolProperty("init.svc_debug.no_fatal." + name_, false)) {
+                        // Aborts into `fatal_reboot_target_'.
+                        SetFatalRebootTarget(fatal_reboot_target_);
+                        LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
+                                   << exit_reason;
+                    }
                 } else {
                     LOG(ERROR) << "process with updatable components '" << name_
                                << "' exited 4 times " << exit_reason;
diff --git a/init/service.h b/init/service.h
index bc5c90f..aee1e5d 100644
--- a/init/service.h
+++ b/init/service.h
@@ -137,6 +137,7 @@
             flags_ &= ~SVC_ONESHOT;
         }
     }
+    Subcontext* subcontext() const { return subcontext_; }
 
   private:
     void NotifyStateChange(const std::string& new_state) const;
@@ -168,6 +169,7 @@
     std::vector<FileDescriptor> files_;
     std::vector<std::pair<std::string, std::string>> environment_vars_;
 
+    Subcontext* subcontext_;
     Action onrestart_;  // Commands to execute on restart.
 
     std::vector<std::string> writepid_files_;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 97621da..57c311a 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -657,6 +657,14 @@
                            << "' with a config in APEX";
         }
 
+        std::string context = service_->subcontext() ? service_->subcontext()->context() : "";
+        std::string old_context =
+                old_service->subcontext() ? old_service->subcontext()->context() : "";
+        if (context != old_context) {
+            return Error() << "service '" << service_->name() << "' overrides another service "
+                           << "across the treble boundary.";
+        }
+
         service_list_->RemoveService(*old_service);
         old_service = nullptr;
     }
diff --git a/init/service_utils.h b/init/service_utils.h
index e74f8c1..1e0b4bd 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -37,6 +37,8 @@
     Descriptor(const std::string& name, android::base::unique_fd fd)
         : name_(name), fd_(std::move(fd)){};
 
+    // Publish() unsets FD_CLOEXEC from the FD and publishes its name via setenv().  It should be
+    // called when starting a service after fork() and before exec().
     void Publish() const;
 
   private:
@@ -53,6 +55,9 @@
     std::string context;
     bool passcred = false;
 
+    // Create() creates the named unix domain socket in /dev/socket and returns a Descriptor object.
+    // It should be called when starting a service, before calling fork(), such that the socket is
+    // synchronously created before starting any other services, which may depend on it.
     Result<Descriptor> Create(const std::string& global_context) const;
 };
 
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
new file mode 100644
index 0000000..19b5c57
--- /dev/null
+++ b/init/snapuserd_transition.cpp
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2020 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 "snapuserd_transition.h"
+
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <filesystem>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+#include <libsnapshot/snapshot.h>
+#include <libsnapshot/snapuserd_client.h>
+#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+
+#include "block_dev_initializer.h"
+#include "service_utils.h"
+#include "util.h"
+
+namespace android {
+namespace init {
+
+using namespace std::string_literals;
+
+using android::base::unique_fd;
+using android::snapshot::SnapshotManager;
+using android::snapshot::SnapuserdClient;
+
+static constexpr char kSnapuserdPath[] = "/system/bin/snapuserd";
+static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID";
+static constexpr char kSnapuserdFirstStageFdVar[] = "FIRST_STAGE_SNAPUSERD_FD";
+static constexpr char kSnapuserdLabel[] = "u:object_r:snapuserd_exec:s0";
+static constexpr char kSnapuserdSocketLabel[] = "u:object_r:snapuserd_socket:s0";
+
+void LaunchFirstStageSnapuserd() {
+    SocketDescriptor socket_desc;
+    socket_desc.name = android::snapshot::kSnapuserdSocket;
+    socket_desc.type = SOCK_STREAM;
+    socket_desc.perm = 0660;
+    socket_desc.uid = AID_SYSTEM;
+    socket_desc.gid = AID_SYSTEM;
+
+    // We specify a label here even though it technically is not needed. During
+    // first_stage_mount there is no sepolicy loaded. Once sepolicy is loaded,
+    // we bypass the socket entirely.
+    auto socket = socket_desc.Create(kSnapuserdSocketLabel);
+    if (!socket.ok()) {
+        LOG(FATAL) << "Could not create snapuserd socket: " << socket.error();
+    }
+
+    pid_t pid = fork();
+    if (pid < 0) {
+        PLOG(FATAL) << "Cannot launch snapuserd; fork failed";
+    }
+    if (pid == 0) {
+        socket->Publish();
+        char arg0[] = "/system/bin/snapuserd";
+        char* const argv[] = {arg0, nullptr};
+        if (execv(arg0, argv) < 0) {
+            PLOG(FATAL) << "Cannot launch snapuserd; execv failed";
+        }
+        _exit(127);
+    }
+
+    setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
+
+    LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
+}
+
+std::optional<pid_t> GetSnapuserdFirstStagePid() {
+    const char* pid_str = getenv(kSnapuserdFirstStagePidVar);
+    if (!pid_str) {
+        return {};
+    }
+
+    int pid = 0;
+    if (!android::base::ParseInt(pid_str, &pid)) {
+        LOG(FATAL) << "Could not parse pid in environment, " << kSnapuserdFirstStagePidVar << "="
+                   << pid_str;
+    }
+    return {pid};
+}
+
+static void RelabelLink(const std::string& link) {
+    selinux_android_restorecon(link.c_str(), 0);
+
+    std::string path;
+    if (android::base::Readlink(link, &path)) {
+        selinux_android_restorecon(path.c_str(), 0);
+    }
+}
+
+static void RelabelDeviceMapper() {
+    selinux_android_restorecon("/dev/device-mapper", 0);
+
+    std::error_code ec;
+    for (auto& iter : std::filesystem::directory_iterator("/dev/block", ec)) {
+        const auto& path = iter.path();
+        if (android::base::StartsWith(path.string(), "/dev/block/dm-")) {
+            selinux_android_restorecon(path.string().c_str(), 0);
+        }
+    }
+}
+
+static std::optional<int> GetRamdiskSnapuserdFd() {
+    const char* fd_str = getenv(kSnapuserdFirstStageFdVar);
+    if (!fd_str) {
+        return {};
+    }
+
+    int fd;
+    if (!android::base::ParseInt(fd_str, &fd)) {
+        LOG(FATAL) << "Could not parse fd in environment, " << kSnapuserdFirstStageFdVar << "="
+                   << fd_str;
+    }
+    return {fd};
+}
+
+void RestoreconRamdiskSnapuserd(int fd) {
+    if (fsetxattr(fd, XATTR_NAME_SELINUX, kSnapuserdLabel, strlen(kSnapuserdLabel) + 1, 0) < 0) {
+        PLOG(FATAL) << "fsetxattr snapuserd failed";
+    }
+}
+
+SnapuserdSelinuxHelper::SnapuserdSelinuxHelper(std::unique_ptr<SnapshotManager>&& sm, pid_t old_pid)
+    : sm_(std::move(sm)), old_pid_(old_pid) {
+    // Only dm-user device names change during transitions, so the other
+    // devices are expected to be present.
+    sm_->SetUeventRegenCallback([this](const std::string& device) -> bool {
+        if (android::base::StartsWith(device, "/dev/dm-user/")) {
+            return block_dev_init_.InitDmUser(android::base::Basename(device));
+        }
+        return true;
+    });
+}
+
+void SnapuserdSelinuxHelper::StartTransition() {
+    LOG(INFO) << "Starting SELinux transition of snapuserd";
+
+    // The restorecon path reads from /system etc, so make sure any reads have
+    // been cached before proceeding.
+    auto handle = selinux_android_file_context_handle();
+    if (!handle) {
+        LOG(FATAL) << "Could not create SELinux file context handle";
+    }
+    selinux_android_set_sehandle(handle);
+
+    // We cannot access /system after the transition, so make sure init is
+    // pinned in memory.
+    if (mlockall(MCL_CURRENT) < 0) {
+        LOG(FATAL) << "mlockall failed";
+    }
+
+    argv_.emplace_back("snapuserd");
+    argv_.emplace_back("-no_socket");
+    if (!sm_->DetachSnapuserdForSelinux(&argv_)) {
+        LOG(FATAL) << "Could not perform selinux transition";
+    }
+
+    // Make sure the process is gone so we don't have any selinux audits.
+    KillFirstStageSnapuserd(old_pid_);
+}
+
+void SnapuserdSelinuxHelper::FinishTransition() {
+    RelabelLink("/dev/block/by-name/super");
+    RelabelDeviceMapper();
+
+    selinux_android_restorecon("/dev/null", 0);
+    selinux_android_restorecon("/dev/urandom", 0);
+    selinux_android_restorecon("/dev/kmsg", 0);
+    selinux_android_restorecon("/dev/dm-user", SELINUX_ANDROID_RESTORECON_RECURSE);
+
+    RelaunchFirstStageSnapuserd();
+
+    if (munlockall() < 0) {
+        PLOG(ERROR) << "munlockall failed";
+    }
+}
+
+void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() {
+    auto fd = GetRamdiskSnapuserdFd();
+    if (!fd) {
+        LOG(FATAL) << "Environment variable " << kSnapuserdFirstStageFdVar << " was not set!";
+    }
+    unsetenv(kSnapuserdFirstStageFdVar);
+
+    RestoreconRamdiskSnapuserd(fd.value());
+
+    pid_t pid = fork();
+    if (pid < 0) {
+        PLOG(FATAL) << "Fork to relaunch snapuserd failed";
+    }
+    if (pid > 0) {
+        // We don't need the descriptor anymore, and it should be closed to
+        // avoid leaking into subprocesses.
+        close(fd.value());
+
+        setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
+
+        LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
+        return;
+    }
+
+    // Make sure the descriptor is gone after we exec.
+    if (fcntl(fd.value(), F_SETFD, FD_CLOEXEC) < 0) {
+        PLOG(FATAL) << "fcntl FD_CLOEXEC failed for snapuserd fd";
+    }
+
+    std::vector<char*> argv;
+    for (auto& arg : argv_) {
+        argv.emplace_back(arg.data());
+    }
+    argv.emplace_back(nullptr);
+
+    int rv = syscall(SYS_execveat, fd.value(), "", reinterpret_cast<char* const*>(argv.data()),
+                     nullptr, AT_EMPTY_PATH);
+    if (rv < 0) {
+        PLOG(FATAL) << "Failed to execveat() snapuserd";
+    }
+}
+
+std::unique_ptr<SnapuserdSelinuxHelper> SnapuserdSelinuxHelper::CreateIfNeeded() {
+    if (IsRecoveryMode()) {
+        return nullptr;
+    }
+
+    auto old_pid = GetSnapuserdFirstStagePid();
+    if (!old_pid) {
+        return nullptr;
+    }
+
+    auto sm = SnapshotManager::NewForFirstStageMount();
+    if (!sm) {
+        LOG(FATAL) << "Unable to create SnapshotManager";
+    }
+    return std::make_unique<SnapuserdSelinuxHelper>(std::move(sm), old_pid.value());
+}
+
+void KillFirstStageSnapuserd(pid_t pid) {
+    if (kill(pid, SIGTERM) < 0 && errno != ESRCH) {
+        LOG(ERROR) << "Kill snapuserd pid failed: " << pid;
+    } else {
+        LOG(INFO) << "Sent SIGTERM to snapuserd process " << pid;
+    }
+}
+
+void CleanupSnapuserdSocket() {
+    auto socket_path = ANDROID_SOCKET_DIR "/"s + android::snapshot::kSnapuserdSocket;
+    if (access(socket_path.c_str(), F_OK) != 0) {
+        return;
+    }
+
+    // Tell the daemon to stop accepting connections and to gracefully exit
+    // once all outstanding handlers have terminated.
+    if (auto client = SnapuserdClient::Connect(android::snapshot::kSnapuserdSocket, 3s)) {
+        client->DetachSnapuserd();
+    }
+
+    // Unlink the socket so we can create it again in second-stage.
+    if (unlink(socket_path.c_str()) < 0) {
+        PLOG(FATAL) << "unlink " << socket_path << " failed";
+    }
+}
+
+void SaveRamdiskPathToSnapuserd() {
+    int fd = open(kSnapuserdPath, O_PATH);
+    if (fd < 0) {
+        PLOG(FATAL) << "Unable to open snapuserd: " << kSnapuserdPath;
+    }
+
+    auto value = std::to_string(fd);
+    if (setenv(kSnapuserdFirstStageFdVar, value.c_str(), 1) < 0) {
+        PLOG(FATAL) << "setenv failed: " << kSnapuserdFirstStageFdVar << "=" << value;
+    }
+}
+
+bool IsFirstStageSnapuserdRunning() {
+    return GetSnapuserdFirstStagePid().has_value();
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/snapuserd_transition.h b/init/snapuserd_transition.h
new file mode 100644
index 0000000..a5ab652
--- /dev/null
+++ b/init/snapuserd_transition.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <libsnapshot/snapshot.h>
+
+#include "block_dev_initializer.h"
+
+namespace android {
+namespace init {
+
+// Fork and exec a new copy of snapuserd.
+void LaunchFirstStageSnapuserd();
+
+class SnapuserdSelinuxHelper final {
+    using SnapshotManager = android::snapshot::SnapshotManager;
+
+  public:
+    SnapuserdSelinuxHelper(std::unique_ptr<SnapshotManager>&& sm, pid_t old_pid);
+
+    void StartTransition();
+    void FinishTransition();
+
+    // Return a helper for facilitating the selinux transition of snapuserd.
+    // If snapuserd is not in use, null is returned. StartTransition() should
+    // be called after reading policy. FinishTransition() should be called
+    // after loading policy. In between, no reads of /system or other dynamic
+    // partitions are possible.
+    static std::unique_ptr<SnapuserdSelinuxHelper> CreateIfNeeded();
+
+  private:
+    void RelaunchFirstStageSnapuserd();
+    void ExecSnapuserd();
+
+    std::unique_ptr<SnapshotManager> sm_;
+    BlockDevInitializer block_dev_init_;
+    pid_t old_pid_;
+    std::vector<std::string> argv_;
+};
+
+// Remove /dev/socket/snapuserd. This ensures that (1) the existing snapuserd
+// will receive no new requests, and (2) the next copy we transition to can
+// own the socket.
+void CleanupSnapuserdSocket();
+
+// Kill an instance of snapuserd given a pid.
+void KillFirstStageSnapuserd(pid_t pid);
+
+// Save an open fd to /system/bin (in the ramdisk) into an environment. This is
+// used to later execveat() snapuserd.
+void SaveRamdiskPathToSnapuserd();
+
+// Returns true if first-stage snapuserd is running.
+bool IsFirstStageSnapuserdRunning();
+
+// Return the pid of the first-stage instances of snapuserd, if it was started.
+std::optional<pid_t> GetSnapuserdFirstStagePid();
+
+// Save an open fd to /system/bin (in the ramdisk) into an environment. This is
+// used to later execveat() snapuserd.
+void SaveRamdiskPathToSnapuserd();
+
+// Returns true if first-stage snapuserd is running.
+bool IsFirstStageSnapuserdRunning();
+
+}  // namespace init
+}  // namespace android
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index dc2455e..f1fbffe 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -342,6 +342,9 @@
                 new Subcontext(std::vector<std::string>{"/vendor", "/odm"}, kVendorContext));
     }
 }
+void InitializeHostSubcontext(std::vector<std::string> vendor_prefixes) {
+    subcontext.reset(new Subcontext(vendor_prefixes, kVendorContext, /*host=*/true));
+}
 
 Subcontext* GetSubcontext() {
     return subcontext.get();
diff --git a/init/subcontext.h b/init/subcontext.h
index 788d3be..cb4138e 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -36,9 +36,11 @@
 
 class Subcontext {
   public:
-    Subcontext(std::vector<std::string> path_prefixes, std::string context)
+    Subcontext(std::vector<std::string> path_prefixes, std::string context, bool host = false)
         : path_prefixes_(std::move(path_prefixes)), context_(std::move(context)), pid_(0) {
-        Fork();
+        if (!host) {
+            Fork();
+        }
     }
 
     Result<void> Execute(const std::vector<std::string>& args);
@@ -61,6 +63,7 @@
 
 int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map);
 void InitializeSubcontext();
+void InitializeHostSubcontext(std::vector<std::string> vendor_prefixes);
 Subcontext* GetSubcontext();
 bool SubcontextChildReap(pid_t pid);
 void SubcontextTerminate();
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index ee765a7..da1f455 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -202,6 +202,8 @@
 
     // For RecoverAfterAbort
     auto do_cause_log_fatal = [](const BuiltinArguments& args) -> Result<void> {
+        // Since this is an expected failure, disable debuggerd to not generate a tombstone.
+        signal(SIGABRT, SIG_DFL);
         return Error() << std::string(4097, 'f');
     };
     auto do_generate_sane_error = [](const BuiltinArguments& args) -> Result<void> {
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 54659c5..923d769 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -283,12 +283,7 @@
 
     std::vector<std::unique_ptr<UeventHandler>> uevent_handlers;
 
-    // Keep the current product name base configuration so we remain backwards compatible and
-    // allow it to override everything.
-    auto hardware = android::base::GetProperty("ro.hardware", "");
-
-    auto ueventd_configuration = ParseConfig({"/system/etc/ueventd.rc", "/vendor/ueventd.rc",
-                                              "/odm/ueventd.rc", "/ueventd." + hardware + ".rc"});
+    auto ueventd_configuration = ParseConfig("/system/etc/ueventd.rc");
 
     uevent_handlers.emplace_back(std::make_unique<DeviceHandler>(
             std::move(ueventd_configuration.dev_permissions),
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 09dce44..2605158 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -21,6 +21,7 @@
 
 #include <android-base/parseint.h>
 
+#include "import_parser.h"
 #include "keyword_map.h"
 #include "parser.h"
 
@@ -33,12 +34,12 @@
                                   std::vector<SysfsPermissions>* out_sysfs_permissions,
                                   std::vector<Permissions>* out_dev_permissions) {
     bool is_sysfs = out_sysfs_permissions != nullptr;
-    if (is_sysfs && args.size() != 5) {
-        return Error() << "/sys/ lines must have 5 entries";
+    if (is_sysfs && !(args.size() == 5 || args.size() == 6)) {
+        return Error() << "/sys/ lines must have 5 or 6 entries";
     }
 
-    if (!is_sysfs && args.size() != 4) {
-        return Error() << "/dev/ lines must have 4 entries";
+    if (!is_sysfs && !(args.size() == 4 || args.size() == 5)) {
+        return Error() << "/dev/ lines must have 4 or 5 entries";
     }
 
     auto it = args.begin();
@@ -69,10 +70,19 @@
     }
     gid_t gid = grp->gr_gid;
 
+    bool no_fnm_pathname = false;
+    if (it != args.end()) {
+        std::string& flags = *it++;
+        if (flags != "no_fnm_pathname") {
+            return Error() << "invalid option '" << flags << "', only no_fnm_pathname is supported";
+        }
+        no_fnm_pathname = true;
+    }
+
     if (is_sysfs) {
-        out_sysfs_permissions->emplace_back(name, sysfs_attribute, perm, uid, gid);
+        out_sysfs_permissions->emplace_back(name, sysfs_attribute, perm, uid, gid, no_fnm_pathname);
     } else {
-        out_dev_permissions->emplace_back(name, perm, uid, gid);
+        out_dev_permissions->emplace_back(name, perm, uid, gid, no_fnm_pathname);
     }
     return {};
 }
@@ -220,10 +230,11 @@
     return {};
 }
 
-UeventdConfiguration ParseConfig(const std::vector<std::string>& configs) {
+UeventdConfiguration ParseConfig(const std::string& config) {
     Parser parser;
     UeventdConfiguration ueventd_configuration;
 
+    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
     parser.AddSectionParser("subsystem",
                             std::make_unique<SubsystemParser>(&ueventd_configuration.subsystems));
 
@@ -249,9 +260,7 @@
                                std::bind(ParseEnabledDisabledLine, _1,
                                          &ueventd_configuration.enable_parallel_restorecon));
 
-    for (const auto& config : configs) {
-        parser.ParseConfig(config);
-    }
+    parser.ParseConfig(config);
 
     return ueventd_configuration;
 }
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index eaafa5a..2672626 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -36,7 +36,7 @@
     bool enable_parallel_restorecon = false;
 };
 
-UeventdConfiguration ParseConfig(const std::vector<std::string>& configs);
+UeventdConfiguration ParseConfig(const std::string& config);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
index 172ba0b..b604c53 100644
--- a/init/ueventd_parser_test.cpp
+++ b/init/ueventd_parser_test.cpp
@@ -104,21 +104,21 @@
 /dev/graphics/*           0660   root       graphics
 /dev/*/test               0660   root       system
 
-/sys/devices/platform/trusty.*      trusty_version        0440  root   log
-/sys/devices/virtual/input/input   enable      0660  root   input
-/sys/devices/virtual/*/input   poll_delay  0660  root   input
+/sys/devices/platform/trusty.*      trusty_version    0440  root   log
+/sys/devices/virtual/input/input    enable            0660  root   input
+/sys/devices/virtual/*/input        poll_delay        0660  root   input    no_fnm_pathname
 )";
 
     auto permissions = std::vector<Permissions>{
-            {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM},
-            {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS},
-            {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM},
+            {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM, false},
+            {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS, false},
+            {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM, false},
     };
 
     auto sysfs_permissions = std::vector<SysfsPermissions>{
-            {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG},
-            {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT},
-            {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT},
+            {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG, false},
+            {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT, false},
+            {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT, true},
     };
 
     TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}, {}});
@@ -240,7 +240,7 @@
     dirname /dev/graphics
 
 /dev/*/test               0660   root       system
-/sys/devices/virtual/*/input   poll_delay  0660  root   input
+/sys/devices/virtual/*/input   poll_delay  0660  root   input    no_fnm_pathname
 firmware_directories /more
 
 external_firmware_handler /devices/path/firmware/firmware001.bin root /vendor/bin/touch.sh
@@ -259,15 +259,15 @@
             {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
 
     auto permissions = std::vector<Permissions>{
-            {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM},
-            {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS},
-            {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM},
+            {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM, false},
+            {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS, false},
+            {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM, false},
     };
 
     auto sysfs_permissions = std::vector<SysfsPermissions>{
-            {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG},
-            {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT},
-            {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT},
+            {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG, false},
+            {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT, false},
+            {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT, true},
     };
 
     auto firmware_directories = std::vector<std::string>{
@@ -299,6 +299,7 @@
 /sys/devices/platform/trusty.*      trusty_version        badmode  root   log
 /sys/devices/platform/trusty.*      trusty_version        0440  baduidbad   log
 /sys/devices/platform/trusty.*      trusty_version        0440  root   baduidbad
+/sys/devices/platform/trusty.*      trusty_version        0440  root   root    bad_option
 
 uevent_socket_rcvbuf_size blah
 
diff --git a/init/util.cpp b/init/util.cpp
index aec3173..255434a 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -30,9 +30,7 @@
 #include <time.h>
 #include <unistd.h>
 
-#include <mutex>
 #include <thread>
-#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -724,50 +722,5 @@
     return access("/system/bin/recovery", F_OK) == 0;
 }
 
-// TODO(b/155203339): remove this
-// Devices in the lab seem to be stuck during shutdown, but the logs don't capture the last actions
-// before shutdown started, so we record those lines, ignoring requests to shutdown, and replay them
-// if we identify that the device is stuck.
-constexpr size_t kRecordedLogsSize = 30;
-std::string recorded_logs[kRecordedLogsSize];
-size_t recorded_log_position = 0;
-std::mutex recorded_logs_lock;
-
-void InitSecondStageLogging(char** argv) {
-    SetFatalRebootTarget();
-    auto second_stage_logger = [](android::base::LogId log_id, android::base::LogSeverity severity,
-                                  const char* tag, const char* file, unsigned int line,
-                                  const char* message) {
-        // We only store logs for init, not its children, and only if they're not related to
-        // sys.powerctl.
-        if (getpid() == 1 && strstr(message, "sys.powerctl") == nullptr) {
-            auto lock = std::lock_guard{recorded_logs_lock};
-            recorded_logs[recorded_log_position++] = message;
-            if (recorded_log_position == kRecordedLogsSize) {
-                recorded_log_position = 0;
-            }
-        }
-        android::base::KernelLogger(log_id, severity, tag, file, line, message);
-    };
-    android::base::InitLogging(argv, second_stage_logger, InitAborter);
-}
-
-void DumpShutdownDebugInformation() {
-    auto lock = std::lock_guard{recorded_logs_lock};
-    android::base::KernelLogger(
-            android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
-            "===================== Dumping previous init lines =====================");
-    for (size_t i = recorded_log_position; i < kRecordedLogsSize; ++i) {
-        android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
-                                    recorded_logs[i].c_str());
-    }
-    for (size_t i = 0; i < recorded_log_position; ++i) {
-        android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
-                                    recorded_logs[i].c_str());
-    }
-    android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
-                                "===================== End of dump =====================");
-}
-
 }  // namespace init
 }  // namespace android
diff --git a/init/util.h b/init/util.h
index 8a6aa60..3cdc9f4 100644
--- a/init/util.h
+++ b/init/util.h
@@ -98,8 +98,6 @@
 
 void SetStdioToDevNull(char** argv);
 void InitKernelLogging(char** argv);
-void InitSecondStageLogging(char** argv);
-void DumpShutdownDebugInformation();
 bool IsRecoveryMode();
 }  // namespace init
 }  // namespace android
diff --git a/libappfuse/tests/FuseAppLoopTest.cc b/libappfuse/tests/FuseAppLoopTest.cc
index 98e3665..ea98ae2 100644
--- a/libappfuse/tests/FuseAppLoopTest.cc
+++ b/libappfuse/tests/FuseAppLoopTest.cc
@@ -167,7 +167,7 @@
   EXPECT_EQ(0u, response_.entry_out.attr.gid);
   EXPECT_EQ(0u, response_.entry_out.attr.rdev);
   EXPECT_EQ(0u, response_.entry_out.attr.blksize);
-  EXPECT_EQ(0u, response_.entry_out.attr.padding);
+  EXPECT_EQ(0u, response_.entry_out.attr.flags);
 }
 
 TEST_F(FuseAppLoopTest, LookUp_InvalidName) {
@@ -226,7 +226,7 @@
   EXPECT_EQ(0u, response_.attr_out.attr.gid);
   EXPECT_EQ(0u, response_.attr_out.attr.rdev);
   EXPECT_EQ(0u, response_.attr_out.attr.blksize);
-  EXPECT_EQ(0u, response_.attr_out.attr.padding);
+  EXPECT_EQ(0u, response_.attr_out.attr.flags);
 }
 
 TEST_F(FuseAppLoopTest, GetAttr_Root) {
@@ -259,7 +259,7 @@
   EXPECT_EQ(0u, response_.attr_out.attr.gid);
   EXPECT_EQ(0u, response_.attr_out.attr.rdev);
   EXPECT_EQ(0u, response_.attr_out.attr.blksize);
-  EXPECT_EQ(0u, response_.attr_out.attr.padding);
+  EXPECT_EQ(0u, response_.attr_out.attr.flags);
 }
 
 TEST_F(FuseAppLoopTest, Open) {
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 284c0b9..d46aeab 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -32,6 +32,7 @@
 cc_library_headers {
     name: "libcutils_headers",
     vendor_available: true,
+    product_available: true,
     recovery_available: true,
     ramdisk_available: true,
     vendor_ramdisk_available: true,
@@ -45,7 +46,10 @@
     export_include_dirs: ["include"],
     target: {
         vendor: {
-            override_export_include_dirs: ["include_vndk"],
+            override_export_include_dirs: ["include_outside_system"],
+        },
+        product: {
+            override_export_include_dirs: ["include_outside_system"],
         },
         linux_bionic: {
             enabled: true,
@@ -60,6 +64,7 @@
 cc_library {
     name: "libcutils_sockets",
     vendor_available: true,
+    product_available: true,
     recovery_available: true,
     ramdisk_available: true,
     vendor_ramdisk_available: true,
@@ -143,6 +148,7 @@
 cc_library {
     name: "libcutils",
     vendor_available: true,
+    product_available: true,
     vndk: {
         enabled: true,
         support_system_process: true,
@@ -204,9 +210,6 @@
                 "uevent.cpp",
             ],
         },
-        bionic: {
-            header_libs: ["bionic_libc_platform_headers"],
-        },
 
         android_arm: {
             sanitize: {
@@ -234,14 +237,19 @@
             },
         },
 
+        // qtaguid.cpp loads libnetd_client.so with dlopen().  Since
+        // the interface of libnetd_client.so may vary between AOSP
+        // releases, exclude qtaguid.cpp from the VNDK-SP variant.
         vendor: {
             exclude_srcs: [
-                // qtaguid.cpp loads libnetd_client.so with dlopen().  Since
-                // the interface of libnetd_client.so may vary between AOSP
-                // releases, exclude qtaguid.cpp from the VNDK-SP variant.
                 "qtaguid.cpp",
             ],
-        }
+        },
+        product: {
+            exclude_srcs: [
+                "qtaguid.cpp",
+            ],
+        },
     },
 
     whole_static_libs: ["libcutils_sockets"],
diff --git a/libcutils/OWNERS b/libcutils/OWNERS
index c18ed51..7529cb9 100644
--- a/libcutils/OWNERS
+++ b/libcutils/OWNERS
@@ -1,4 +1 @@
-cferris@google.com
-enh@google.com
-jmgao@google.com
-tomcherry@google.com
+include platform/system/core:/janitors/OWNERS
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 31e1679..79c3abc 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -203,9 +203,14 @@
                                            CAP_MASK_LONG(CAP_SETGID),
                                               "system/bin/simpleperf_app_runner" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/e2fsck" },
-    { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/tune2fs" },
+#ifdef __LP64__
+    { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/linker64" },
+#else
+    { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/linker" },
+#endif
     { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/resize2fs" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/snapuserd" },
+    { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/tune2fs" },
     // generic defaults
     { 00755, AID_ROOT,      AID_ROOT,      0, "bin/*" },
     { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
diff --git a/libcutils/include/cutils/threads.h b/libcutils/include/cutils/threads.h
index ba4846e..0f7f8a8 100644
--- a/libcutils/include/cutils/threads.h
+++ b/libcutils/include/cutils/threads.h
@@ -14,15 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_CUTILS_THREADS_H
-#define _LIBS_CUTILS_THREADS_H
+#pragma once
 
 #include  <sys/types.h>
 
-#if !defined(_WIN32)
-#include <pthread.h>
-#else
+#if defined(_WIN32)
 #include <windows.h>
+#else
+#include <pthread.h>
 #endif
 
 #ifdef __cplusplus
@@ -32,46 +31,8 @@
 //
 // Deprecated: use android::base::GetThreadId instead, which doesn't truncate on Mac/Windows.
 //
-
 extern pid_t gettid();
 
-//
-// Deprecated: use `_Thread_local` in C or `thread_local` in C++.
-//
-
-#if !defined(_WIN32)
-
-typedef struct {
-    pthread_mutex_t   lock;
-    int               has_tls;
-    pthread_key_t     tls;
-} thread_store_t;
-
-#define  THREAD_STORE_INITIALIZER  { PTHREAD_MUTEX_INITIALIZER, 0, 0 }
-
-#else // !defined(_WIN32)
-
-typedef struct {
-    int               lock_init;
-    int               has_tls;
-    DWORD             tls;
-    CRITICAL_SECTION  lock;
-} thread_store_t;
-
-#define  THREAD_STORE_INITIALIZER  { 0, 0, 0, {0, 0, 0, 0, 0, 0} }
-
-#endif // !defined(_WIN32)
-
-typedef void  (*thread_store_destruct_t)(void*  value);
-
-extern void*  thread_store_get(thread_store_t*  store);
-
-extern void   thread_store_set(thread_store_t*          store,
-                               void*                    value,
-                               thread_store_destruct_t  destroy);
-
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* _LIBS_CUTILS_THREADS_H */
diff --git a/libcutils/include_vndk/cutils/android_filesystem_config.h b/libcutils/include_outside_system/cutils/android_filesystem_config.h
similarity index 100%
rename from libcutils/include_vndk/cutils/android_filesystem_config.h
rename to libcutils/include_outside_system/cutils/android_filesystem_config.h
diff --git a/libcutils/include_vndk/cutils/android_get_control_file.h b/libcutils/include_outside_system/cutils/android_get_control_file.h
similarity index 100%
rename from libcutils/include_vndk/cutils/android_get_control_file.h
rename to libcutils/include_outside_system/cutils/android_get_control_file.h
diff --git a/libcutils/include_vndk/cutils/android_reboot.h b/libcutils/include_outside_system/cutils/android_reboot.h
similarity index 100%
rename from libcutils/include_vndk/cutils/android_reboot.h
rename to libcutils/include_outside_system/cutils/android_reboot.h
diff --git a/libcutils/include_vndk/cutils/ashmem.h b/libcutils/include_outside_system/cutils/ashmem.h
similarity index 100%
rename from libcutils/include_vndk/cutils/ashmem.h
rename to libcutils/include_outside_system/cutils/ashmem.h
diff --git a/libcutils/include_vndk/cutils/atomic.h b/libcutils/include_outside_system/cutils/atomic.h
similarity index 100%
rename from libcutils/include_vndk/cutils/atomic.h
rename to libcutils/include_outside_system/cutils/atomic.h
diff --git a/libcutils/include_vndk/cutils/bitops.h b/libcutils/include_outside_system/cutils/bitops.h
similarity index 100%
rename from libcutils/include_vndk/cutils/bitops.h
rename to libcutils/include_outside_system/cutils/bitops.h
diff --git a/libcutils/include_vndk/cutils/compiler.h b/libcutils/include_outside_system/cutils/compiler.h
similarity index 100%
rename from libcutils/include_vndk/cutils/compiler.h
rename to libcutils/include_outside_system/cutils/compiler.h
diff --git a/libcutils/include_vndk/cutils/config_utils.h b/libcutils/include_outside_system/cutils/config_utils.h
similarity index 100%
rename from libcutils/include_vndk/cutils/config_utils.h
rename to libcutils/include_outside_system/cutils/config_utils.h
diff --git a/libcutils/include_vndk/cutils/fs.h b/libcutils/include_outside_system/cutils/fs.h
similarity index 100%
rename from libcutils/include_vndk/cutils/fs.h
rename to libcutils/include_outside_system/cutils/fs.h
diff --git a/libcutils/include_vndk/cutils/hashmap.h b/libcutils/include_outside_system/cutils/hashmap.h
similarity index 100%
rename from libcutils/include_vndk/cutils/hashmap.h
rename to libcutils/include_outside_system/cutils/hashmap.h
diff --git a/libcutils/include_vndk/cutils/iosched_policy.h b/libcutils/include_outside_system/cutils/iosched_policy.h
similarity index 100%
rename from libcutils/include_vndk/cutils/iosched_policy.h
rename to libcutils/include_outside_system/cutils/iosched_policy.h
diff --git a/libcutils/include_vndk/cutils/klog.h b/libcutils/include_outside_system/cutils/klog.h
similarity index 100%
rename from libcutils/include_vndk/cutils/klog.h
rename to libcutils/include_outside_system/cutils/klog.h
diff --git a/libcutils/include_vndk/cutils/list.h b/libcutils/include_outside_system/cutils/list.h
similarity index 100%
rename from libcutils/include_vndk/cutils/list.h
rename to libcutils/include_outside_system/cutils/list.h
diff --git a/libcutils/include_vndk/cutils/log.h b/libcutils/include_outside_system/cutils/log.h
similarity index 100%
rename from libcutils/include_vndk/cutils/log.h
rename to libcutils/include_outside_system/cutils/log.h
diff --git a/libcutils/include_vndk/cutils/memory.h b/libcutils/include_outside_system/cutils/memory.h
similarity index 100%
rename from libcutils/include_vndk/cutils/memory.h
rename to libcutils/include_outside_system/cutils/memory.h
diff --git a/libcutils/include_vndk/cutils/misc.h b/libcutils/include_outside_system/cutils/misc.h
similarity index 100%
rename from libcutils/include_vndk/cutils/misc.h
rename to libcutils/include_outside_system/cutils/misc.h
diff --git a/libcutils/include_vndk/cutils/multiuser.h b/libcutils/include_outside_system/cutils/multiuser.h
similarity index 100%
rename from libcutils/include_vndk/cutils/multiuser.h
rename to libcutils/include_outside_system/cutils/multiuser.h
diff --git a/libcutils/include_vndk/cutils/native_handle.h b/libcutils/include_outside_system/cutils/native_handle.h
similarity index 100%
rename from libcutils/include_vndk/cutils/native_handle.h
rename to libcutils/include_outside_system/cutils/native_handle.h
diff --git a/libcutils/include_vndk/cutils/partition_utils.h b/libcutils/include_outside_system/cutils/partition_utils.h
similarity index 100%
rename from libcutils/include_vndk/cutils/partition_utils.h
rename to libcutils/include_outside_system/cutils/partition_utils.h
diff --git a/libcutils/include_vndk/cutils/properties.h b/libcutils/include_outside_system/cutils/properties.h
similarity index 100%
rename from libcutils/include_vndk/cutils/properties.h
rename to libcutils/include_outside_system/cutils/properties.h
diff --git a/libcutils/include_vndk/cutils/qtaguid.h b/libcutils/include_outside_system/cutils/qtaguid.h
similarity index 100%
rename from libcutils/include_vndk/cutils/qtaguid.h
rename to libcutils/include_outside_system/cutils/qtaguid.h
diff --git a/libcutils/include_vndk/cutils/record_stream.h b/libcutils/include_outside_system/cutils/record_stream.h
similarity index 100%
rename from libcutils/include_vndk/cutils/record_stream.h
rename to libcutils/include_outside_system/cutils/record_stream.h
diff --git a/libcutils/include_vndk/cutils/sched_policy.h b/libcutils/include_outside_system/cutils/sched_policy.h
similarity index 100%
rename from libcutils/include_vndk/cutils/sched_policy.h
rename to libcutils/include_outside_system/cutils/sched_policy.h
diff --git a/libcutils/include_vndk/cutils/sockets.h b/libcutils/include_outside_system/cutils/sockets.h
similarity index 100%
rename from libcutils/include_vndk/cutils/sockets.h
rename to libcutils/include_outside_system/cutils/sockets.h
diff --git a/libcutils/include_vndk/cutils/str_parms.h b/libcutils/include_outside_system/cutils/str_parms.h
similarity index 100%
rename from libcutils/include_vndk/cutils/str_parms.h
rename to libcutils/include_outside_system/cutils/str_parms.h
diff --git a/libcutils/include_vndk/cutils/threads.h b/libcutils/include_outside_system/cutils/threads.h
similarity index 100%
rename from libcutils/include_vndk/cutils/threads.h
rename to libcutils/include_outside_system/cutils/threads.h
diff --git a/libcutils/include_vndk/cutils/trace.h b/libcutils/include_outside_system/cutils/trace.h
similarity index 100%
rename from libcutils/include_vndk/cutils/trace.h
rename to libcutils/include_outside_system/cutils/trace.h
diff --git a/libcutils/include_vndk/cutils/uevent.h b/libcutils/include_outside_system/cutils/uevent.h
similarity index 100%
rename from libcutils/include_vndk/cutils/uevent.h
rename to libcutils/include_outside_system/cutils/uevent.h
diff --git a/libcutils/memory.cpp b/libcutils/memory.cpp
index f526520..5a410c2 100644
--- a/libcutils/memory.cpp
+++ b/libcutils/memory.cpp
@@ -18,19 +18,18 @@
 
 #include <log/log.h>
 
-#ifdef __BIONIC__
-#include <bionic/malloc.h>
+#if !defined(__APPLE__)
+#include <malloc.h>
 #endif
 
 void process_disable_memory_mitigations() {
     bool success = false;
 #ifdef __BIONIC__
-    // TODO(b/158870657) is fixed and scudo is used globally, we can assert when an
-    // an error is returned.
-
-    success = android_mallopt(M_DISABLE_MEMORY_MITIGATIONS, nullptr, 0);
+    success = mallopt(M_BIONIC_DISABLE_MEMORY_MITIGATIONS, 0);
 #endif
 
+    // TODO: if b/158870657 is fixed and scudo is used globally,
+    // we can assert on failure rather than just log.
     if (success) {
         ALOGI("Disabled memory mitigations for process.");
     } else {
diff --git a/libcutils/threads.cpp b/libcutils/threads.cpp
index a7e6b2d..8cfee1e 100644
--- a/libcutils/threads.cpp
+++ b/libcutils/threads.cpp
@@ -16,23 +16,18 @@
 
 #include <cutils/threads.h>
 
-// For gettid.
 #if defined(__APPLE__)
-#include "AvailabilityMacros.h"  // For MAC_OS_X_VERSION_MAX_ALLOWED
 #include <stdint.h>
-#include <stdlib.h>
-#include <sys/syscall.h>
-#include <sys/time.h>
-#include <unistd.h>
-#elif defined(__linux__) && !defined(__ANDROID__)
+#elif defined(__linux__)
 #include <syscall.h>
 #include <unistd.h>
 #elif defined(_WIN32)
 #include <windows.h>
 #endif
 
+#if defined(__BIONIC__)
 // No definition needed for Android because we'll just pick up bionic's copy.
-#ifndef __ANDROID__
+#else
 pid_t gettid() {
 #if defined(__APPLE__)
   uint64_t tid;
@@ -44,68 +39,4 @@
   return GetCurrentThreadId();
 #endif
 }
-#endif  // __ANDROID__
-
-#if !defined(_WIN32)
-
-void*  thread_store_get( thread_store_t*  store )
-{
-    if (!store->has_tls)
-        return NULL;
-
-    return pthread_getspecific( store->tls );
-}
-
-extern void   thread_store_set( thread_store_t*          store,
-                                void*                    value,
-                                thread_store_destruct_t  destroy)
-{
-    pthread_mutex_lock( &store->lock );
-    if (!store->has_tls) {
-        if (pthread_key_create( &store->tls, destroy) != 0) {
-            pthread_mutex_unlock(&store->lock);
-            return;
-        }
-        store->has_tls = 1;
-    }
-    pthread_mutex_unlock( &store->lock );
-
-    pthread_setspecific( store->tls, value );
-}
-
-#else /* !defined(_WIN32) */
-void*  thread_store_get( thread_store_t*  store )
-{
-    if (!store->has_tls)
-        return NULL;
-
-    return (void*) TlsGetValue( store->tls );
-}
-
-void   thread_store_set( thread_store_t*          store,
-                         void*                    value,
-                         thread_store_destruct_t  /*destroy*/ )
-{
-    /* XXX: can't use destructor on thread exit */
-    if (!store->lock_init) {
-        store->lock_init = -1;
-        InitializeCriticalSection( &store->lock );
-        store->lock_init = -2;
-    } else while (store->lock_init != -2) {
-        Sleep(10); /* 10ms */
-    }
-
-    EnterCriticalSection( &store->lock );
-    if (!store->has_tls) {
-        store->tls = TlsAlloc();
-        if (store->tls == TLS_OUT_OF_INDEXES) {
-            LeaveCriticalSection( &store->lock );
-            return;
-        }
-        store->has_tls = 1;
-    }
-    LeaveCriticalSection( &store->lock );
-
-    TlsSetValue( store->tls, value );
-}
-#endif /* !defined(_WIN32) */
+#endif
diff --git a/libmodprobe/OWNERS b/libmodprobe/OWNERS
index 4b770b1..e6b5bba 100644
--- a/libmodprobe/OWNERS
+++ b/libmodprobe/OWNERS
@@ -1,2 +1 @@
-tomcherry@google.com
 smuckle@google.com
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index 65371fa..eec2415 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -21,6 +21,11 @@
     cflags: ["-Werror"],
 
     export_include_dirs: ["include"],
+    // TODO: remove connectivity module dependency, or have this lib build against the ndk
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.tethering",
+    ],
 }
 
 cc_library_static {
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 71e2b91..f104100 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -1,6 +1,7 @@
 cc_library_headers {
     name: "libprocessgroup_headers",
     vendor_available: true,
+    product_available: true,
     ramdisk_available: true,
     vendor_ramdisk_available: true,
     recovery_available: true,
@@ -36,6 +37,7 @@
     vendor_ramdisk_available: true,
     recovery_available: true,
     vendor_available: true,
+    product_available: true,
     vndk: {
         enabled: true,
         support_system_process: true,
diff --git a/libprocessgroup/OWNERS b/libprocessgroup/OWNERS
index 27b9a03..8ebb8cc 100644
--- a/libprocessgroup/OWNERS
+++ b/libprocessgroup/OWNERS
@@ -1,3 +1,2 @@
 ccross@google.com
 surenb@google.com
-tomcherry@google.com
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index b82b0ab..5ca0967 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -71,7 +71,7 @@
     if (!HasValue()) return false;
 
     if (state_ == UNKNOWN) {
-        if (ACgroupController_getFlags != nullptr) {
+        if (__builtin_available(android 30, *)) {
             uint32_t flags = ACgroupController_getFlags(controller_);
             state_ = (flags & CGROUPRC_CONTROLLER_FLAG_MOUNTED) != 0 ? USABLE : MISSING;
         } else {
@@ -172,7 +172,7 @@
     auto controller_count = ACgroupFile_getControllerCount();
     for (uint32_t i = 0; i < controller_count; ++i) {
         const ACgroupController* controller = ACgroupFile_getController(i);
-        if (ACgroupController_getFlags != nullptr) {
+        if (__builtin_available(android 30, *)) {
             LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver "
                       << ACgroupController_getVersion(controller) << " path "
                       << ACgroupController_getPath(controller) << " flags "
diff --git a/libprocessgroup/cgrouprc/include/android/cgrouprc.h b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
index 7e74432..9a79954 100644
--- a/libprocessgroup/cgrouprc/include/android/cgrouprc.h
+++ b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
@@ -28,8 +28,6 @@
 struct ACgroupController;
 typedef struct ACgroupController ACgroupController;
 
-#if __ANDROID_API__ >= __ANDROID_API_Q__
-
 // ACgroupFile
 
 /**
@@ -71,8 +69,6 @@
 #define CGROUPRC_CONTROLLER_FLAG_MOUNTED 0x1
 #define CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION 0x2
 
-#if __ANDROID_API__ >= __ANDROID_API_R__
-
 /**
  * Returns the flags bitmask of the given controller.
  * If the given controller is null, return 0.
@@ -80,8 +76,6 @@
 __attribute__((warn_unused_result, weak)) uint32_t ACgroupController_getFlags(
         const ACgroupController*) __INTRODUCED_IN(30);
 
-#endif
-
 /**
  * Returns the name of the given controller.
  * If the given controller is null, return nullptr.
@@ -97,5 +91,3 @@
         __INTRODUCED_IN(29);
 
 __END_DECLS
-
-#endif
diff --git a/libprocessgroup/include/processgroup/sched_policy.h b/libprocessgroup/include/processgroup/sched_policy.h
index 945d90c..a18847e 100644
--- a/libprocessgroup/include/processgroup/sched_policy.h
+++ b/libprocessgroup/include/processgroup/sched_policy.h
@@ -42,7 +42,7 @@
     SP_DEFAULT = -1,
     SP_BACKGROUND = 0,
     SP_FOREGROUND = 1,
-    SP_SYSTEM = 2,  // can't be used with set_sched_policy()
+    SP_SYSTEM = 2,
     SP_AUDIO_APP = 3,
     SP_AUDIO_SYS = 4,
     SP_TOP_APP = 5,
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
index c371ef7..a496237 100644
--- a/libprocessgroup/profiles/Android.bp
+++ b/libprocessgroup/profiles/Android.bp
@@ -15,6 +15,11 @@
 prebuilt_etc {
     name: "cgroups.json",
     src: "cgroups.json",
+    required: [
+        "cgroups_28.json",
+        "cgroups_29.json",
+        "cgroups_30.json",
+    ],
 }
 
 prebuilt_etc {
@@ -25,8 +30,49 @@
 }
 
 prebuilt_etc {
+    name: "cgroups_28.json",
+    src: "cgroups_28.json",
+    sub_dir: "task_profiles",
+}
+
+prebuilt_etc {
+    name: "cgroups_29.json",
+    src: "cgroups_29.json",
+    sub_dir: "task_profiles",
+}
+
+prebuilt_etc {
+    name: "cgroups_30.json",
+    src: "cgroups_30.json",
+    sub_dir: "task_profiles",
+}
+
+prebuilt_etc {
     name: "task_profiles.json",
     src: "task_profiles.json",
+    required: [
+        "task_profiles_28.json",
+        "task_profiles_29.json",
+        "task_profiles_30.json",
+    ],
+}
+
+prebuilt_etc {
+    name: "task_profiles_28.json",
+    src: "task_profiles_28.json",
+    sub_dir: "task_profiles",
+}
+
+prebuilt_etc {
+    name: "task_profiles_29.json",
+    src: "task_profiles_29.json",
+    sub_dir: "task_profiles",
+}
+
+prebuilt_etc {
+    name: "task_profiles_30.json",
+    src: "task_profiles_30.json",
+    sub_dir: "task_profiles",
 }
 
 cc_defaults {
diff --git a/libprocessgroup/profiles/cgroups_28.json b/libprocessgroup/profiles/cgroups_28.json
new file mode 100644
index 0000000..17d4929
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups_28.json
@@ -0,0 +1,11 @@
+{
+  "Cgroups": [
+    {
+      "Controller": "schedtune",
+      "Path": "/dev/stune",
+      "Mode": "0755",
+      "UID": "system",
+      "GID": "system"
+    }
+  ]
+}
diff --git a/libprocessgroup/profiles/cgroups_29.json b/libprocessgroup/profiles/cgroups_29.json
new file mode 100644
index 0000000..17d4929
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups_29.json
@@ -0,0 +1,11 @@
+{
+  "Cgroups": [
+    {
+      "Controller": "schedtune",
+      "Path": "/dev/stune",
+      "Mode": "0755",
+      "UID": "system",
+      "GID": "system"
+    }
+  ]
+}
diff --git a/libprocessgroup/profiles/cgroups_30.json b/libprocessgroup/profiles/cgroups_30.json
new file mode 100644
index 0000000..17d4929
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups_30.json
@@ -0,0 +1,11 @@
+{
+  "Cgroups": [
+    {
+      "Controller": "schedtune",
+      "Path": "/dev/stune",
+      "Mode": "0755",
+      "UID": "system",
+      "GID": "system"
+    }
+  ]
+}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index ea0064f..5b57bdd 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -100,7 +100,20 @@
           "Params":
           {
             "Controller": "cpu",
-            "Path": ""
+            "Path": "system"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "ServicePerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpu",
+            "Path": "system-background"
           }
         }
       ]
@@ -591,6 +604,10 @@
       "Profiles": [ "MaxPerformance", "MaxIoPriority", "TimerSlackNormal" ]
     },
     {
+      "Name": "SCHED_SP_SYSTEM",
+      "Profiles": [ "ServicePerformance", "LowIoPriority", "TimerSlackNormal" ]
+    },
+    {
       "Name": "SCHED_SP_RT_APP",
       "Profiles": [ "RealtimePerformance", "MaxIoPriority", "TimerSlackNormal" ]
     },
diff --git a/libprocessgroup/profiles/task_profiles_28.json b/libprocessgroup/profiles/task_profiles_28.json
new file mode 100644
index 0000000..9f83785
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles_28.json
@@ -0,0 +1,135 @@
+{
+  "Attributes": [
+    {
+      "Name": "STuneBoost",
+      "Controller": "schedtune",
+      "File": "schedtune.boost"
+    },
+    {
+      "Name": "STunePreferIdle",
+      "Controller": "schedtune",
+      "File": "schedtune.prefer_idle"
+    }
+  ],
+
+  "Profiles": [
+    {
+      "Name": "HighEnergySaving",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "background"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "NormalPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": ""
+          }
+        }
+      ]
+    },
+    {
+      "Name": "HighPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "foreground"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "MaxPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "top-app"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "RealtimePerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "rt"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "CameraServicePerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "camera-daemon"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "NNApiHALPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "nnapi-hal"
+          }
+        }
+      ]
+    },
+
+    {
+      "Name": "CpuPolicySpread",
+      "Actions": [
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "STunePreferIdle",
+            "Value": "1"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "CpuPolicyPack",
+      "Actions": [
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "STunePreferIdle",
+            "Value": "0"
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/libprocessgroup/profiles/task_profiles_29.json b/libprocessgroup/profiles/task_profiles_29.json
new file mode 100644
index 0000000..9f83785
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles_29.json
@@ -0,0 +1,135 @@
+{
+  "Attributes": [
+    {
+      "Name": "STuneBoost",
+      "Controller": "schedtune",
+      "File": "schedtune.boost"
+    },
+    {
+      "Name": "STunePreferIdle",
+      "Controller": "schedtune",
+      "File": "schedtune.prefer_idle"
+    }
+  ],
+
+  "Profiles": [
+    {
+      "Name": "HighEnergySaving",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "background"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "NormalPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": ""
+          }
+        }
+      ]
+    },
+    {
+      "Name": "HighPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "foreground"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "MaxPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "top-app"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "RealtimePerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "rt"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "CameraServicePerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "camera-daemon"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "NNApiHALPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "nnapi-hal"
+          }
+        }
+      ]
+    },
+
+    {
+      "Name": "CpuPolicySpread",
+      "Actions": [
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "STunePreferIdle",
+            "Value": "1"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "CpuPolicyPack",
+      "Actions": [
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "STunePreferIdle",
+            "Value": "0"
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/libprocessgroup/profiles/task_profiles_30.json b/libprocessgroup/profiles/task_profiles_30.json
new file mode 100644
index 0000000..9f83785
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles_30.json
@@ -0,0 +1,135 @@
+{
+  "Attributes": [
+    {
+      "Name": "STuneBoost",
+      "Controller": "schedtune",
+      "File": "schedtune.boost"
+    },
+    {
+      "Name": "STunePreferIdle",
+      "Controller": "schedtune",
+      "File": "schedtune.prefer_idle"
+    }
+  ],
+
+  "Profiles": [
+    {
+      "Name": "HighEnergySaving",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "background"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "NormalPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": ""
+          }
+        }
+      ]
+    },
+    {
+      "Name": "HighPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "foreground"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "MaxPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "top-app"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "RealtimePerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "rt"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "CameraServicePerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "camera-daemon"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "NNApiHALPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "nnapi-hal"
+          }
+        }
+      ]
+    },
+
+    {
+      "Name": "CpuPolicySpread",
+      "Actions": [
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "STunePreferIdle",
+            "Value": "1"
+          }
+        }
+      ]
+    },
+    {
+      "Name": "CpuPolicyPack",
+      "Actions": [
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "STunePreferIdle",
+            "Value": "0"
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 698e74d..c51ee61 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -124,6 +124,8 @@
             return SetTaskProfiles(tid, {"SCHED_SP_FOREGROUND"}, true) ? 0 : -1;
         case SP_TOP_APP:
             return SetTaskProfiles(tid, {"SCHED_SP_TOP_APP"}, true) ? 0 : -1;
+        case SP_SYSTEM:
+            return SetTaskProfiles(tid, {"SCHED_SP_SYSTEM"}, true) ? 0 : -1;
         case SP_RT_APP:
             return SetTaskProfiles(tid, {"SCHED_SP_RT_APP"}, true) ? 0 : -1;
         default:
@@ -258,7 +260,7 @@
      */
     static constexpr const char* kSchedProfiles[SP_CNT + 1] = {
             "SCHED_SP_DEFAULT", "SCHED_SP_BACKGROUND", "SCHED_SP_FOREGROUND",
-            "SCHED_SP_DEFAULT", "SCHED_SP_FOREGROUND", "SCHED_SP_FOREGROUND",
+            "SCHED_SP_SYSTEM",  "SCHED_SP_FOREGROUND", "SCHED_SP_FOREGROUND",
             "SCHED_SP_TOP_APP", "SCHED_SP_RT_APP",     "SCHED_SP_DEFAULT"};
     if (policy < SP_DEFAULT || policy >= SP_CNT) {
         return nullptr;
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index 25f16a6..753fd2d 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -45,7 +45,7 @@
 
 #include "cgroup_descriptor.h"
 
-using android::base::GetBoolProperty;
+using android::base::GetUintProperty;
 using android::base::StringPrintf;
 using android::base::unique_fd;
 
@@ -55,6 +55,8 @@
 static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json";
 static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json";
 
+static constexpr const char* TEMPLATE_CGROUPS_DESC_API_FILE = "/etc/task_profiles/cgroups_%u.json";
+
 static bool ChangeDirModeAndOwner(const std::string& path, mode_t mode, const std::string& uid,
                                   const std::string& gid, bool permissive_mode = false) {
     uid_t pw_uid = -1;
@@ -217,6 +219,18 @@
         return false;
     }
 
+    // load API-level specific system cgroups descriptors if available
+    unsigned int api_level = GetUintProperty<unsigned int>("ro.product.first_api_level", 0);
+    if (api_level > 0) {
+        std::string api_cgroups_path =
+                android::base::StringPrintf(TEMPLATE_CGROUPS_DESC_API_FILE, api_level);
+        if (!access(api_cgroups_path.c_str(), F_OK) || errno != ENOENT) {
+            if (!ReadDescriptorsFromFile(api_cgroups_path, descriptors)) {
+                return false;
+            }
+        }
+    }
+
     // load vendor cgroup descriptors if the file exists
     if (!access(CGROUPS_DESC_VENDOR_FILE, F_OK) &&
         !ReadDescriptorsFromFile(CGROUPS_DESC_VENDOR_FILE, descriptors)) {
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 4e767db..1311306 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -23,6 +23,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/threads.h>
@@ -38,13 +39,17 @@
 #endif
 
 using android::base::GetThreadId;
+using android::base::GetUintProperty;
 using android::base::StringPrintf;
 using android::base::StringReplace;
 using android::base::unique_fd;
 using android::base::WriteStringToFile;
 
-#define TASK_PROFILE_DB_FILE "/etc/task_profiles.json"
-#define TASK_PROFILE_DB_VENDOR_FILE "/vendor/etc/task_profiles.json"
+static constexpr const char* TASK_PROFILE_DB_FILE = "/etc/task_profiles.json";
+static constexpr const char* TASK_PROFILE_DB_VENDOR_FILE = "/vendor/etc/task_profiles.json";
+
+static constexpr const char* TEMPLATE_TASK_PROFILE_API_FILE =
+        "/etc/task_profiles/task_profiles_%u.json";
 
 void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) {
     controller_ = controller;
@@ -391,6 +396,19 @@
         LOG(ERROR) << "Loading " << TASK_PROFILE_DB_FILE << " for [" << getpid() << "] failed";
     }
 
+    // load API-level specific system task profiles if available
+    unsigned int api_level = GetUintProperty<unsigned int>("ro.product.first_api_level", 0);
+    if (api_level > 0) {
+        std::string api_profiles_path =
+                android::base::StringPrintf(TEMPLATE_TASK_PROFILE_API_FILE, api_level);
+        if (!access(api_profiles_path.c_str(), F_OK) || errno != ENOENT) {
+            if (!Load(CgroupMap::GetInstance(), api_profiles_path)) {
+                LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid()
+                           << "] failed";
+            }
+        }
+    }
+
     // load vendor task profiles if the file exists
     if (!access(TASK_PROFILE_DB_VENDOR_FILE, F_OK) &&
         !Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_VENDOR_FILE)) {
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index bf06bbc..5b6ce41 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -4,6 +4,7 @@
     name: "libsparse",
     host_supported: true,
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     unique_host_soname: true,
     vendor_available: true,
@@ -27,6 +28,10 @@
             enabled: true,
         },
     },
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.virt",
+    ],
 }
 
 cc_binary {
diff --git a/libstats/pull/Android.bp b/libstats/pull/Android.bp
deleted file mode 100644
index a8b4a4f..0000000
--- a/libstats/pull/Android.bp
+++ /dev/null
@@ -1,110 +0,0 @@
-//
-// Copyright (C) 2019 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.
-//
-
-// ==========================================================
-// Native library to register a pull atom callback with statsd
-// ==========================================================
-cc_defaults {
-    name: "libstatspull_defaults",
-    srcs: [
-        "stats_pull_atom_callback.cpp",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-    export_include_dirs: ["include"],
-    shared_libs: [
-        "libbinder_ndk",
-        "liblog",
-        "libstatssocket",
-    ],
-    static_libs: [
-        "libutils",
-        "statsd-aidl-ndk_platform",
-    ],
-}
-cc_library_shared {
-    name: "libstatspull",
-    defaults: [
-        "libstatspull_defaults"
-    ],
-    // enumerate stable entry points for APEX use
-    stubs: {
-        symbol_file: "libstatspull.map.txt",
-        versions: [
-            "30",
-        ],
-    },
-    apex_available: [
-        "com.android.os.statsd",
-        "test_com.android.os.statsd",
-    ],
-
-    stl: "libc++_static",
-
-    // TODO(b/151102177): Enable it when the build error is fixed.
-    header_abi_checker: {
-        enabled: false,
-    },
-}
-
-// ONLY USE IN TESTS.
-cc_library_static {
-    name: "libstatspull_private",
-    defaults: [
-        "libstatspull_defaults",
-    ],
-    visibility: [
-        "//frameworks/base/apex/statsd/tests/libstatspull",
-    ],
-}
-
-// Note: These unit tests only test PullAtomMetadata.
-// For full E2E tests of libstatspull, use LibStatsPullTests
-cc_test {
-    name: "libstatspull_test",
-    srcs: [
-        "tests/pull_atom_metadata_test.cpp",
-    ],
-    shared_libs: [
-        "libstatspull",
-        "libstatssocket",
-    ],
-    test_suites: ["general-tests", "mts"],
-    test_config: "libstatspull_test.xml",
-
-    //TODO(b/153588990): Remove when the build system properly separates 
-    //32bit and 64bit architectures.
-    compile_multilib: "both",
-    multilib: {
-        lib64: {
-            suffix: "64",
-        },
-        lib32: {
-            suffix: "32",
-        },
-    },
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wno-missing-field-initializers",
-        "-Wno-unused-variable",
-        "-Wno-unused-function",
-        "-Wno-unused-parameter",
-    ],
-    require_root: true,
-}
diff --git a/libstats/pull/OWNERS b/libstats/pull/OWNERS
deleted file mode 100644
index 7855774..0000000
--- a/libstats/pull/OWNERS
+++ /dev/null
@@ -1,7 +0,0 @@
-joeo@google.com
-muhammadq@google.com
-ruchirr@google.com
-singhtejinder@google.com
-tsaichristine@google.com
-yaochen@google.com
-yro@google.com
diff --git a/libstats/pull/TEST_MAPPING b/libstats/pull/TEST_MAPPING
deleted file mode 100644
index 76f4f02..0000000
--- a/libstats/pull/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "presubmit" : [
-    {
-      "name" : "libstatspull_test"
-    }
-  ]
-}
\ No newline at end of file
diff --git a/libstats/pull/include/stats_pull_atom_callback.h b/libstats/pull/include/stats_pull_atom_callback.h
deleted file mode 100644
index 17df584..0000000
--- a/libstats/pull/include/stats_pull_atom_callback.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <stats_event.h>
-
-#include <stdbool.h>
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Opaque struct representing the metadata for registering an AStatsManager_PullAtomCallback.
- */
-struct AStatsManager_PullAtomMetadata;
-typedef struct AStatsManager_PullAtomMetadata AStatsManager_PullAtomMetadata;
-
-/**
- * Allocate and initialize new PullAtomMetadata.
- *
- * Must call AStatsManager_PullAtomMetadata_release to free the memory.
- */
-AStatsManager_PullAtomMetadata* AStatsManager_PullAtomMetadata_obtain();
-
-/**
- * Frees the memory held by this PullAtomMetadata
- *
- * After calling this, the PullAtomMetadata must not be used or modified in any way.
- */
-void AStatsManager_PullAtomMetadata_release(AStatsManager_PullAtomMetadata* metadata);
-
-/**
- * Set the cool down time of the pull in milliseconds. If two successive pulls are issued
- * within the cool down, a cached version of the first will be used for the second. The minimum
- * allowed cool down is one second.
- */
-void AStatsManager_PullAtomMetadata_setCoolDownMillis(AStatsManager_PullAtomMetadata* metadata,
-                                                      int64_t cool_down_millis);
-
-/**
- * Get the cool down time of the pull in milliseconds.
- */
-int64_t AStatsManager_PullAtomMetadata_getCoolDownMillis(AStatsManager_PullAtomMetadata* metadata);
-
-/**
- * Set the maximum time the pull can take in milliseconds.
- * The maximum allowed timeout is 10 seconds.
- */
-void AStatsManager_PullAtomMetadata_setTimeoutMillis(AStatsManager_PullAtomMetadata* metadata,
-                                                     int64_t timeout_millis);
-
-/**
- * Get the maximum time the pull can take in milliseconds.
- */
-int64_t AStatsManager_PullAtomMetadata_getTimeoutMillis(AStatsManager_PullAtomMetadata* metadata);
-
-/**
- * Set the additive fields of this pulled atom.
- *
- * This is only applicable for atoms which have a uid field. When tasks are run in
- * isolated processes, the data will be attributed to the host uid. Additive fields
- * will be combined when the non-additive fields are the same.
- */
-void AStatsManager_PullAtomMetadata_setAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
-                                                      int32_t* additive_fields, int32_t num_fields);
-
-/**
- * Get the number of additive fields for this pulled atom. This is intended to be called before
- * AStatsManager_PullAtomMetadata_getAdditiveFields to determine the size of the array.
- */
-int32_t AStatsManager_PullAtomMetadata_getNumAdditiveFields(
-        AStatsManager_PullAtomMetadata* metadata);
-
-/**
- * Get the additive fields of this pulled atom.
- *
- * \param fields an output parameter containing the additive fields for this PullAtomMetadata.
- *               Fields is an array and it is assumed that it is at least as large as the number of
- *               additive fields, which can be obtained by calling
- *               AStatsManager_PullAtomMetadata_getNumAdditiveFields.
- */
-void AStatsManager_PullAtomMetadata_getAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
-                                                      int32_t* fields);
-
-/**
- * Return codes for the result of a pull.
- */
-typedef int32_t AStatsManager_PullAtomCallbackReturn;
-enum {
-    // Value indicating that this pull was successful and that the result should be used.
-    AStatsManager_PULL_SUCCESS = 0,
-    // Value indicating that this pull was unsuccessful and that the result should not be used.
-    AStatsManager_PULL_SKIP = 1,
-};
-
-/**
- * Opaque struct representing a list of AStatsEvent objects.
- */
-struct AStatsEventList;
-typedef struct AStatsEventList AStatsEventList;
-
-/**
- * Appends and returns an AStatsEvent to the end of the AStatsEventList.
- *
- * If an AStatsEvent is obtained in this manner, the memory is internally managed and
- * AStatsEvent_release does not need to be called. The lifetime of the AStatsEvent is that of the
- * AStatsEventList.
- *
- * The AStatsEvent does still need to be built by calling AStatsEvent_build.
- */
-AStatsEvent* AStatsEventList_addStatsEvent(AStatsEventList* pull_data);
-
-/**
- * Callback interface for pulling atoms requested by the stats service.
- *
- * \param atom_tag the tag of the atom to pull.
- * \param data an output parameter in which the caller should fill the results of the pull. This
- *             param cannot be NULL and it's lifetime is as long as the execution of the callback.
- *             It must not be accessed or modified after returning from the callback.
- * \param cookie the opaque pointer passed in AStatsManager_registerPullAtomCallback.
- * \return AStatsManager_PULL_SUCCESS if the pull was successful, or AStatsManager_PULL_SKIP if not.
- */
-typedef AStatsManager_PullAtomCallbackReturn (*AStatsManager_PullAtomCallback)(
-        int32_t atom_tag, AStatsEventList* data, void* cookie);
-/**
- * Sets a callback for an atom when that atom is to be pulled. The stats service will
- * invoke the callback when the stats service determines that this atom needs to be
- * pulled.
- *
- * Requires the REGISTER_STATS_PULL_ATOM permission.
- *
- * \param atom_tag          The tag of the atom for this pull atom callback.
- * \param metadata          Optional metadata specifying the timeout, cool down time, and
- *                          additive fields for mapping isolated to host uids.
- *                          This param is nullable, in which case defaults will be used.
- * \param callback          The callback to be invoked when the stats service pulls the atom.
- * \param cookie            A pointer that will be passed back to the callback.
- *                          It has no meaning to statsd.
- */
-void AStatsManager_setPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata* metadata,
-                                       AStatsManager_PullAtomCallback callback, void* cookie);
-
-/**
- * Clears a callback for an atom when that atom is to be pulled. Note that any ongoing
- * pulls will still occur.
- *
- * Requires the REGISTER_STATS_PULL_ATOM permission.
- *
- * \param atomTag           The tag of the atom of which to unregister
- */
-void AStatsManager_clearPullAtomCallback(int32_t atom_tag);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/libstats/pull/libstatspull.map.txt b/libstats/pull/libstatspull.map.txt
deleted file mode 100644
index e0e851a..0000000
--- a/libstats/pull/libstatspull.map.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-LIBSTATSPULL {
-    global:
-        AStatsManager_PullAtomMetadata_obtain; # apex # introduced=30
-        AStatsManager_PullAtomMetadata_release; # apex # introduced=30
-        AStatsManager_PullAtomMetadata_setCoolDownMillis; # apex # introduced=30
-        AStatsManager_PullAtomMetadata_getCoolDownMillis; # apex # introduced=30
-        AStatsManager_PullAtomMetadata_setTimeoutMillis; # apex # introduced=30
-        AStatsManager_PullAtomMetadata_getTimeoutMillis; # apex # introduced=30
-        AStatsManager_PullAtomMetadata_setAdditiveFields; # apex # introduced=30
-        AStatsManager_PullAtomMetadata_getNumAdditiveFields; # apex # introduced=30
-        AStatsManager_PullAtomMetadata_getAdditiveFields; # apex # introduced=30
-        AStatsEventList_addStatsEvent; # apex # introduced=30
-        AStatsManager_setPullAtomCallback; # apex # introduced=30
-        AStatsManager_clearPullAtomCallback; # apex # introduced=30
-    local:
-        *;
-};
diff --git a/libstats/pull/libstatspull_test.xml b/libstats/pull/libstatspull_test.xml
deleted file mode 100644
index 233fc1f..0000000
--- a/libstats/pull/libstatspull_test.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<configuration description="Runs libstatspull_test.">
-    <option name="test-suite-tag" value="apct" />
-    <option name="test-suite-tag" value="apct-native" />
-    <option name="test-suite-tag" value="mts" />
-
-    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
-
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
-       <option name="cleanup" value="true" />
-       <option name="push" value="libstatspull_test->/data/local/tmp/libstatspull_test" />
-       <option name="append-bitness" value="true" />
-    </target_preparer>
-
-    <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/local/tmp" />
-        <option name="module-name" value="libstatspull_test" />
-    </test>
-
-    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
-        <option name="mainline-module-package-name" value="com.google.android.os.statsd" />
-    </object>
-</configuration>
diff --git a/libstats/pull/stats_pull_atom_callback.cpp b/libstats/pull/stats_pull_atom_callback.cpp
deleted file mode 100644
index 478cae7..0000000
--- a/libstats/pull/stats_pull_atom_callback.cpp
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright (C) 2019, 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 <map>
-#include <thread>
-#include <vector>
-
-#include <stats_event.h>
-#include <stats_pull_atom_callback.h>
-
-#include <aidl/android/os/BnPullAtomCallback.h>
-#include <aidl/android/os/IPullAtomResultReceiver.h>
-#include <aidl/android/os/IStatsd.h>
-#include <aidl/android/util/StatsEventParcel.h>
-#include <android/binder_auto_utils.h>
-#include <android/binder_ibinder.h>
-#include <android/binder_manager.h>
-
-using Status = ::ndk::ScopedAStatus;
-using aidl::android::os::BnPullAtomCallback;
-using aidl::android::os::IPullAtomResultReceiver;
-using aidl::android::os::IStatsd;
-using aidl::android::util::StatsEventParcel;
-using ::ndk::SharedRefBase;
-
-struct AStatsEventList {
-    std::vector<AStatsEvent*> data;
-};
-
-AStatsEvent* AStatsEventList_addStatsEvent(AStatsEventList* pull_data) {
-    AStatsEvent* event = AStatsEvent_obtain();
-    pull_data->data.push_back(event);
-    return event;
-}
-
-static const int64_t DEFAULT_COOL_DOWN_MILLIS = 1000LL;  // 1 second.
-static const int64_t DEFAULT_TIMEOUT_MILLIS = 2000LL;    // 2 seconds.
-
-struct AStatsManager_PullAtomMetadata {
-    int64_t cool_down_millis;
-    int64_t timeout_millis;
-    std::vector<int32_t> additive_fields;
-};
-
-AStatsManager_PullAtomMetadata* AStatsManager_PullAtomMetadata_obtain() {
-    AStatsManager_PullAtomMetadata* metadata = new AStatsManager_PullAtomMetadata();
-    metadata->cool_down_millis = DEFAULT_COOL_DOWN_MILLIS;
-    metadata->timeout_millis = DEFAULT_TIMEOUT_MILLIS;
-    metadata->additive_fields = std::vector<int32_t>();
-    return metadata;
-}
-
-void AStatsManager_PullAtomMetadata_release(AStatsManager_PullAtomMetadata* metadata) {
-    delete metadata;
-}
-
-void AStatsManager_PullAtomMetadata_setCoolDownMillis(AStatsManager_PullAtomMetadata* metadata,
-                                                      int64_t cool_down_millis) {
-    metadata->cool_down_millis = cool_down_millis;
-}
-
-int64_t AStatsManager_PullAtomMetadata_getCoolDownMillis(AStatsManager_PullAtomMetadata* metadata) {
-    return metadata->cool_down_millis;
-}
-
-void AStatsManager_PullAtomMetadata_setTimeoutMillis(AStatsManager_PullAtomMetadata* metadata,
-                                                     int64_t timeout_millis) {
-    metadata->timeout_millis = timeout_millis;
-}
-
-int64_t AStatsManager_PullAtomMetadata_getTimeoutMillis(AStatsManager_PullAtomMetadata* metadata) {
-    return metadata->timeout_millis;
-}
-
-void AStatsManager_PullAtomMetadata_setAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
-                                                      int32_t* additive_fields,
-                                                      int32_t num_fields) {
-    metadata->additive_fields.assign(additive_fields, additive_fields + num_fields);
-}
-
-int32_t AStatsManager_PullAtomMetadata_getNumAdditiveFields(
-        AStatsManager_PullAtomMetadata* metadata) {
-    return metadata->additive_fields.size();
-}
-
-void AStatsManager_PullAtomMetadata_getAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
-                                                      int32_t* fields) {
-    std::copy(metadata->additive_fields.begin(), metadata->additive_fields.end(), fields);
-}
-
-class StatsPullAtomCallbackInternal : public BnPullAtomCallback {
-  public:
-    StatsPullAtomCallbackInternal(const AStatsManager_PullAtomCallback callback, void* cookie,
-                                  const int64_t coolDownMillis, const int64_t timeoutMillis,
-                                  const std::vector<int32_t> additiveFields)
-        : mCallback(callback),
-          mCookie(cookie),
-          mCoolDownMillis(coolDownMillis),
-          mTimeoutMillis(timeoutMillis),
-          mAdditiveFields(additiveFields) {}
-
-    Status onPullAtom(int32_t atomTag,
-                      const std::shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
-        AStatsEventList statsEventList;
-        int successInt = mCallback(atomTag, &statsEventList, mCookie);
-        bool success = successInt == AStatsManager_PULL_SUCCESS;
-
-        // Convert stats_events into StatsEventParcels.
-        std::vector<StatsEventParcel> parcels;
-        for (int i = 0; i < statsEventList.data.size(); i++) {
-            size_t size;
-            uint8_t* buffer = AStatsEvent_getBuffer(statsEventList.data[i], &size);
-
-            StatsEventParcel p;
-            // vector.assign() creates a copy, but this is inevitable unless
-            // stats_event.h/c uses a vector as opposed to a buffer.
-            p.buffer.assign(buffer, buffer + size);
-            parcels.push_back(std::move(p));
-        }
-
-        Status status = resultReceiver->pullFinished(atomTag, success, parcels);
-        if (!status.isOk()) {
-            std::vector<StatsEventParcel> emptyParcels;
-            resultReceiver->pullFinished(atomTag, /*success=*/false, emptyParcels);
-        }
-        for (int i = 0; i < statsEventList.data.size(); i++) {
-            AStatsEvent_release(statsEventList.data[i]);
-        }
-        return Status::ok();
-    }
-
-    int64_t getCoolDownMillis() const { return mCoolDownMillis; }
-    int64_t getTimeoutMillis() const { return mTimeoutMillis; }
-    const std::vector<int32_t>& getAdditiveFields() const { return mAdditiveFields; }
-
-  private:
-    const AStatsManager_PullAtomCallback mCallback;
-    void* mCookie;
-    const int64_t mCoolDownMillis;
-    const int64_t mTimeoutMillis;
-    const std::vector<int32_t> mAdditiveFields;
-};
-
-static std::mutex pullAtomMutex;
-static std::shared_ptr<IStatsd> sStatsd = nullptr;
-
-static std::map<int32_t, std::shared_ptr<StatsPullAtomCallbackInternal>> mPullers;
-static std::shared_ptr<IStatsd> getStatsService();
-
-static void binderDied(void* /*cookie*/) {
-    {
-        std::lock_guard<std::mutex> lock(pullAtomMutex);
-        sStatsd = nullptr;
-    }
-
-    std::shared_ptr<IStatsd> statsService = getStatsService();
-    if (statsService == nullptr) {
-        return;
-    }
-
-    // Since we do not want to make an IPC with the lock held, we first create a
-    // copy of the data with the lock held before iterating through the map.
-    std::map<int32_t, std::shared_ptr<StatsPullAtomCallbackInternal>> pullersCopy;
-    {
-        std::lock_guard<std::mutex> lock(pullAtomMutex);
-        pullersCopy = mPullers;
-    }
-    for (const auto& it : pullersCopy) {
-        statsService->registerNativePullAtomCallback(it.first, it.second->getCoolDownMillis(),
-                                                     it.second->getTimeoutMillis(),
-                                                     it.second->getAdditiveFields(), it.second);
-    }
-}
-
-static ::ndk::ScopedAIBinder_DeathRecipient sDeathRecipient(
-        AIBinder_DeathRecipient_new(binderDied));
-
-static std::shared_ptr<IStatsd> getStatsService() {
-    std::lock_guard<std::mutex> lock(pullAtomMutex);
-    if (!sStatsd) {
-        // Fetch statsd
-        ::ndk::SpAIBinder binder(AServiceManager_getService("stats"));
-        sStatsd = IStatsd::fromBinder(binder);
-        if (sStatsd) {
-            AIBinder_linkToDeath(binder.get(), sDeathRecipient.get(), /*cookie=*/nullptr);
-        }
-    }
-    return sStatsd;
-}
-
-void registerStatsPullAtomCallbackBlocking(int32_t atomTag,
-                                           std::shared_ptr<StatsPullAtomCallbackInternal> cb) {
-    const std::shared_ptr<IStatsd> statsService = getStatsService();
-    if (statsService == nullptr) {
-        // Statsd not available
-        return;
-    }
-
-    statsService->registerNativePullAtomCallback(
-            atomTag, cb->getCoolDownMillis(), cb->getTimeoutMillis(), cb->getAdditiveFields(), cb);
-}
-
-void unregisterStatsPullAtomCallbackBlocking(int32_t atomTag) {
-    const std::shared_ptr<IStatsd> statsService = getStatsService();
-    if (statsService == nullptr) {
-        // Statsd not available
-        return;
-    }
-
-    statsService->unregisterNativePullAtomCallback(atomTag);
-}
-
-void AStatsManager_setPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata* metadata,
-                                       AStatsManager_PullAtomCallback callback, void* cookie) {
-    int64_t coolDownMillis =
-            metadata == nullptr ? DEFAULT_COOL_DOWN_MILLIS : metadata->cool_down_millis;
-    int64_t timeoutMillis = metadata == nullptr ? DEFAULT_TIMEOUT_MILLIS : metadata->timeout_millis;
-
-    std::vector<int32_t> additiveFields;
-    if (metadata != nullptr) {
-        additiveFields = metadata->additive_fields;
-    }
-
-    std::shared_ptr<StatsPullAtomCallbackInternal> callbackBinder =
-            SharedRefBase::make<StatsPullAtomCallbackInternal>(callback, cookie, coolDownMillis,
-                                                               timeoutMillis, additiveFields);
-
-    {
-        std::lock_guard<std::mutex> lg(pullAtomMutex);
-        // Always add to the map. If statsd is dead, we will add them when it comes back.
-        mPullers[atom_tag] = callbackBinder;
-    }
-
-    std::thread registerThread(registerStatsPullAtomCallbackBlocking, atom_tag, callbackBinder);
-    registerThread.detach();
-}
-
-void AStatsManager_clearPullAtomCallback(int32_t atom_tag) {
-    {
-        std::lock_guard<std::mutex> lg(pullAtomMutex);
-        // Always remove the puller from our map.
-        // If statsd is down, we will not register it when it comes back.
-        mPullers.erase(atom_tag);
-    }
-    std::thread unregisterThread(unregisterStatsPullAtomCallbackBlocking, atom_tag);
-    unregisterThread.detach();
-}
diff --git a/libstats/pull/tests/pull_atom_metadata_test.cpp b/libstats/pull/tests/pull_atom_metadata_test.cpp
deleted file mode 100644
index abc8e47..0000000
--- a/libstats/pull/tests/pull_atom_metadata_test.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2020, 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 <gtest/gtest.h>
-
-#include <stats_pull_atom_callback.h>
-
-namespace {
-
-static const int64_t DEFAULT_COOL_DOWN_MILLIS = 1000LL;  // 1 second.
-static const int64_t DEFAULT_TIMEOUT_MILLIS = 2000LL;    // 2 seconds.
-
-}  // anonymous namespace
-
-TEST(AStatsManager_PullAtomMetadataTest, TestEmpty) {
-    AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
-    EXPECT_EQ(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), DEFAULT_COOL_DOWN_MILLIS);
-    EXPECT_EQ(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), DEFAULT_TIMEOUT_MILLIS);
-    EXPECT_EQ(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), 0);
-    AStatsManager_PullAtomMetadata_release(metadata);
-}
-
-TEST(AStatsManager_PullAtomMetadataTest, TestSetTimeoutMillis) {
-    int64_t timeoutMillis = 500;
-    AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
-    AStatsManager_PullAtomMetadata_setTimeoutMillis(metadata, timeoutMillis);
-    EXPECT_EQ(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), DEFAULT_COOL_DOWN_MILLIS);
-    EXPECT_EQ(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), timeoutMillis);
-    EXPECT_EQ(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), 0);
-    AStatsManager_PullAtomMetadata_release(metadata);
-}
-
-TEST(AStatsManager_PullAtomMetadataTest, TestSetCoolDownMillis) {
-    int64_t coolDownMillis = 10000;
-    AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
-    AStatsManager_PullAtomMetadata_setCoolDownMillis(metadata, coolDownMillis);
-    EXPECT_EQ(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), coolDownMillis);
-    EXPECT_EQ(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), DEFAULT_TIMEOUT_MILLIS);
-    EXPECT_EQ(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), 0);
-    AStatsManager_PullAtomMetadata_release(metadata);
-}
-
-TEST(AStatsManager_PullAtomMetadataTest, TestSetAdditiveFields) {
-    const int numFields = 3;
-    int inputFields[numFields] = {2, 4, 6};
-    AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
-    AStatsManager_PullAtomMetadata_setAdditiveFields(metadata, inputFields, numFields);
-    EXPECT_EQ(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), DEFAULT_COOL_DOWN_MILLIS);
-    EXPECT_EQ(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), DEFAULT_TIMEOUT_MILLIS);
-    EXPECT_EQ(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), numFields);
-    int outputFields[numFields];
-    AStatsManager_PullAtomMetadata_getAdditiveFields(metadata, outputFields);
-    for (int i = 0; i < numFields; i++) {
-        EXPECT_EQ(inputFields[i], outputFields[i]);
-    }
-    AStatsManager_PullAtomMetadata_release(metadata);
-}
-
-TEST(AStatsManager_PullAtomMetadataTest, TestSetAllElements) {
-    int64_t timeoutMillis = 500;
-    int64_t coolDownMillis = 10000;
-    const int numFields = 3;
-    int inputFields[numFields] = {2, 4, 6};
-
-    AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
-    AStatsManager_PullAtomMetadata_setTimeoutMillis(metadata, timeoutMillis);
-    AStatsManager_PullAtomMetadata_setCoolDownMillis(metadata, coolDownMillis);
-    AStatsManager_PullAtomMetadata_setAdditiveFields(metadata, inputFields, numFields);
-
-    EXPECT_EQ(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), coolDownMillis);
-    EXPECT_EQ(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), timeoutMillis);
-    EXPECT_EQ(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), numFields);
-    int outputFields[numFields];
-    AStatsManager_PullAtomMetadata_getAdditiveFields(metadata, outputFields);
-    for (int i = 0; i < numFields; i++) {
-        EXPECT_EQ(inputFields[i], outputFields[i]);
-    }
-    AStatsManager_PullAtomMetadata_release(metadata);
-}
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
deleted file mode 100644
index 89cdfe5..0000000
--- a/libstats/socket/Android.bp
+++ /dev/null
@@ -1,125 +0,0 @@
-//
-// Copyright (C) 2018 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.
-//
-
-// =========================================================================
-// Native library to write stats log to statsd socket on Android R and later
-// =========================================================================
-cc_defaults {
-    name: "libstatssocket_defaults",
-    srcs: [
-        "stats_buffer_writer.c",
-        "stats_event.c",
-        "stats_socket.c",
-        "statsd_writer.c",
-    ],
-    export_include_dirs: ["include"],
-    static_libs: [
-        "libcutils", // does not expose a stable C API
-    ],
-    header_libs: ["liblog_headers"],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-}
-
-cc_library {
-    name: "libstatssocket",
-    defaults: [
-        "libstatssocket_defaults",
-    ],
-    host_supported: true,
-    target: {
-        // On android, libstatssocket should only be linked as a shared lib
-        android: {
-            static: {
-                enabled: false,
-            },
-        },
-        host: {
-            shared: {
-                enabled: false,
-            },
-        },
-    },
-    stl: "libc++_static",
-
-    // enumerate stable entry points for APEX use
-    stubs: {
-        symbol_file: "libstatssocket.map.txt",
-        versions: [
-            "30",
-        ],
-    },
-    apex_available: [
-        "com.android.os.statsd",
-        "test_com.android.os.statsd",
-    ],
-}
-
-//TODO (b/149842105): Figure out if there is a better solution for this.
-cc_test_library {
-    name: "libstatssocket_private",
-    defaults: [
-        "libstatssocket_defaults",
-    ],
-    visibility: [
-        "//frameworks/base/apex/statsd/tests/libstatspull",
-        "//frameworks/base/cmds/statsd",
-    ],
-}
-
-cc_library_headers {
-    name: "libstatssocket_headers",
-    export_include_dirs: ["include"],
-    host_supported: true,
-    apex_available: ["com.android.resolv"],
-    min_sdk_version: "29",
-}
-
-cc_test {
-    name: "libstatssocket_test",
-    srcs: [
-        "tests/stats_event_test.cpp",
-        "tests/stats_writer_test.cpp",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-    static_libs: [
-        "libgmock",
-        "libstatssocket_private",
-    ],
-    shared_libs: [
-        "libcutils",
-        "libutils",
-    ],
-    test_suites: ["device-tests", "mts"],
-    test_config: "libstatssocket_test.xml",
-    //TODO(b/153588990): Remove when the build system properly separates.
-    //32bit and 64bit architectures.
-    compile_multilib: "both",
-    multilib: {
-        lib64: {
-            suffix: "64",
-        },
-        lib32: {
-            suffix: "32",
-        },
-    },
-    require_root: true,
-}
diff --git a/libstats/socket/TEST_MAPPING b/libstats/socket/TEST_MAPPING
deleted file mode 100644
index 0224998..0000000
--- a/libstats/socket/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "presubmit" : [
-    {
-      "name" : "libstatssocket_test"
-    }
-  ]
-}
\ No newline at end of file
diff --git a/libstats/socket/include/stats_buffer_writer.h b/libstats/socket/include/stats_buffer_writer.h
deleted file mode 100644
index 5b41f0e..0000000
--- a/libstats/socket/include/stats_buffer_writer.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stddef.h>
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif  // __CPLUSPLUS
-void stats_log_close();
-int stats_log_is_closed();
-int write_buffer_to_statsd(void* buffer, size_t size, uint32_t atomId);
-#ifdef __cplusplus
-}
-#endif  // __CPLUSPLUS
diff --git a/libstats/socket/include/stats_event.h b/libstats/socket/include/stats_event.h
deleted file mode 100644
index 3d3baf9..0000000
--- a/libstats/socket/include/stats_event.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_STATS_LOG_STATS_EVENT_H
-#define ANDROID_STATS_LOG_STATS_EVENT_H
-
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-
-/*
- * Functionality to build and store the buffer sent over the statsd socket.
- * This code defines and encapsulates the socket protocol.
- *
- * Usage:
- *      AStatsEvent* event = AStatsEvent_obtain();
- *
- *      AStatsEvent_setAtomId(event, atomId);
- *      AStatsEvent_addBoolAnnotation(event, 5, false); // atom-level annotation
- *      AStatsEvent_writeInt32(event, 24);
- *      AStatsEvent_addBoolAnnotation(event, 1, true); // annotation for preceding atom field
- *      AStatsEvent_addInt32Annotation(event, 2, 128);
- *      AStatsEvent_writeFloat(event, 2.0);
- *
- *      AStatsEvent_write(event);
- *      AStatsEvent_release(event);
- *
- * Note that calls to add atom fields and annotations should be made in the
- * order that they are defined in the atom.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif  // __CPLUSPLUS
-
-/**
- * Opaque struct use to represent a StatsEvent. It builds and stores the data that is sent to
- * statsd.
- */
-struct AStatsEvent;
-typedef struct AStatsEvent AStatsEvent;
-
-/**
- * Returns a new AStatsEvent. If you call this function, you must call AStatsEvent_release to free
- * the allocated memory.
- */
-AStatsEvent* AStatsEvent_obtain();
-
-/**
- * Builds and finalizes the AStatsEvent for a pulled event.
- * This should only be called for pulled AStatsEvents.
- *
- * After this function, the StatsEvent must not be modified in any way other than calling release or
- * write.
- *
- * Build can be called multiple times without error.
- * If the event has been built before, this function is a no-op.
- */
-void AStatsEvent_build(AStatsEvent* event);
-
-/**
- * Writes the StatsEvent to the stats log.
- *
- * After calling this, AStatsEvent_release must be called,
- * and is the only function that can be safely called.
- */
-int AStatsEvent_write(AStatsEvent* event);
-
-/**
- * Frees the memory held by this StatsEvent.
- *
- * After calling this, the StatsEvent must not be used or modified in any way.
- */
-void AStatsEvent_release(AStatsEvent* event);
-
-/**
- * Sets the atom id for this StatsEvent.
- *
- * This function should be called immediately after AStatsEvent_obtain. It may
- * be called additional times as well, but subsequent calls will have no effect.
- **/
-void AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId);
-
-/**
- * Writes an int32_t field to this StatsEvent.
- **/
-void AStatsEvent_writeInt32(AStatsEvent* event, int32_t value);
-
-/**
- * Writes an int64_t field to this StatsEvent.
- **/
-void AStatsEvent_writeInt64(AStatsEvent* event, int64_t value);
-
-/**
- * Writes a float field to this StatsEvent.
- **/
-void AStatsEvent_writeFloat(AStatsEvent* event, float value);
-
-/**
- * Write a bool field to this StatsEvent.
- **/
-void AStatsEvent_writeBool(AStatsEvent* event, bool value);
-
-/**
- * Write a byte array field to this StatsEvent.
- **/
-void AStatsEvent_writeByteArray(AStatsEvent* event, const uint8_t* buf, size_t numBytes);
-
-/**
- * Write a string field to this StatsEvent.
- *
- * The string must be null-terminated.
- **/
-void AStatsEvent_writeString(AStatsEvent* event, const char* value);
-
-/**
- * Write an attribution chain field to this StatsEvent.
- *
- * The sizes of uids and tags must be equal. The AttributionNode at position i is
- * made up of uids[i] and tags[i].
- *
- * \param uids array of uids in the attribution chain.
- * \param tags array of tags in the attribution chain. Each tag must be null-terminated.
- * \param numNodes the number of AttributionNodes in the attribution chain. This is the length of
- *                 the uids and the tags.
- **/
-void AStatsEvent_writeAttributionChain(AStatsEvent* event, const uint32_t* uids,
-                                       const char* const* tags, uint8_t numNodes);
-
-/**
- * Write a bool annotation for the previous field written.
- **/
-void AStatsEvent_addBoolAnnotation(AStatsEvent* event, uint8_t annotationId, bool value);
-
-/**
- * Write an integer annotation for the previous field written.
- **/
-void AStatsEvent_addInt32Annotation(AStatsEvent* event, uint8_t annotationId, int32_t value);
-
-// Internal/test APIs. Should not be exposed outside of the APEX.
-void AStatsEvent_overwriteTimestamp(AStatsEvent* event, uint64_t timestampNs);
-uint32_t AStatsEvent_getAtomId(AStatsEvent* event);
-// Size is an output parameter.
-uint8_t* AStatsEvent_getBuffer(AStatsEvent* event, size_t* size);
-uint32_t AStatsEvent_getErrors(AStatsEvent* event);
-
-#ifdef __cplusplus
-}
-#endif  // __CPLUSPLUS
-
-#endif  // ANDROID_STATS_LOG_STATS_EVENT_H
diff --git a/libstats/socket/libstatssocket.map.txt b/libstats/socket/libstatssocket.map.txt
deleted file mode 100644
index 5c13904..0000000
--- a/libstats/socket/libstatssocket.map.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-LIBSTATSSOCKET {
-    global:
-        AStatsEvent_obtain; # apex # introduced=30
-        AStatsEvent_build; # apex # introduced=30
-        AStatsEvent_write; # apex # introduced=30
-        AStatsEvent_release; # apex # introduced=30
-        AStatsEvent_setAtomId; # apex # introduced=30
-        AStatsEvent_writeInt32; # apex # introduced=30
-        AStatsEvent_writeInt64; # apex # introduced=30
-        AStatsEvent_writeFloat; # apex # introduced=30
-        AStatsEvent_writeBool; # apex # introduced=30
-        AStatsEvent_writeByteArray; # apex # introduced=30
-        AStatsEvent_writeString; # apex # introduced=30
-        AStatsEvent_writeAttributionChain; # apex # introduced=30
-        AStatsEvent_addBoolAnnotation; # apex # introduced=30
-        AStatsEvent_addInt32Annotation; # apex # introduced=30
-        AStatsSocket_close; # apex # introduced=30
-    local:
-        *;
-};
diff --git a/libstats/socket/libstatssocket_test.xml b/libstats/socket/libstatssocket_test.xml
deleted file mode 100644
index d2694d1..0000000
--- a/libstats/socket/libstatssocket_test.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<configuration description="Runs libstatssocket_test.">
-    <option name="test-suite-tag" value="apct" />
-    <option name="test-suite-tag" value="apct-native" />
-    <option name="test-suite-tag" value="mts" />
-
-    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
-
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
-       <option name="cleanup" value="true" />
-       <option name="push" value="libstatssocket_test->/data/local/tmp/libstatssocket_test" />
-       <option name="append-bitness" value="true" />
-    </target_preparer>
-
-    <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/local/tmp" />
-        <option name="module-name" value="libstatssocket_test" />
-    </test>
-
-    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
-        <option name="mainline-module-package-name" value="com.google.android.os.statsd" />
-    </object>
-</configuration>
-
diff --git a/libstats/socket/stats_buffer_writer.c b/libstats/socket/stats_buffer_writer.c
deleted file mode 100644
index 549acdc..0000000
--- a/libstats/socket/stats_buffer_writer.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2019 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 "include/stats_buffer_writer.h"
-#ifdef __ANDROID__
-#include <cutils/properties.h>
-#endif
-#include <errno.h>
-#include <sys/time.h>
-#include <sys/uio.h>
-#include "statsd_writer.h"
-
-static const uint32_t kStatsEventTag = 1937006964;
-
-extern struct android_log_transport_write statsdLoggerWrite;
-
-static int __write_to_statsd_init(struct iovec* vec, size_t nr);
-static int (*__write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
-
-void note_log_drop(int error, int atomId) {
-    statsdLoggerWrite.noteDrop(error, atomId);
-}
-
-void stats_log_close() {
-    statsd_writer_init_lock();
-    __write_to_statsd = __write_to_statsd_init;
-    if (statsdLoggerWrite.close) {
-        (*statsdLoggerWrite.close)();
-    }
-    statsd_writer_init_unlock();
-}
-
-int stats_log_is_closed() {
-    return statsdLoggerWrite.isClosed && (*statsdLoggerWrite.isClosed)();
-}
-
-int write_buffer_to_statsd(void* buffer, size_t size, uint32_t atomId) {
-    int ret = 1;
-
-    struct iovec vecs[2];
-    vecs[0].iov_base = (void*)&kStatsEventTag;
-    vecs[0].iov_len = sizeof(kStatsEventTag);
-    vecs[1].iov_base = buffer;
-    vecs[1].iov_len = size;
-
-    ret = __write_to_statsd(vecs, 2);
-
-    if (ret < 0) {
-        note_log_drop(ret, atomId);
-    }
-
-    return ret;
-}
-
-static int __write_to_stats_daemon(struct iovec* vec, size_t nr) {
-    int save_errno;
-    struct timespec ts;
-    size_t len, i;
-
-    for (len = i = 0; i < nr; ++i) {
-        len += vec[i].iov_len;
-    }
-    if (!len) {
-        return -EINVAL;
-    }
-
-    save_errno = errno;
-#if defined(__ANDROID__)
-    clock_gettime(CLOCK_REALTIME, &ts);
-#else
-    struct timeval tv;
-    gettimeofday(&tv, NULL);
-    ts.tv_sec = tv.tv_sec;
-    ts.tv_nsec = tv.tv_usec * 1000;
-#endif
-
-    int ret = (int)(*statsdLoggerWrite.write)(&ts, vec, nr);
-    errno = save_errno;
-    return ret;
-}
-
-static int __write_to_statsd_initialize_locked() {
-    if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
-        if (statsdLoggerWrite.close) {
-            (*statsdLoggerWrite.close)();
-            return -ENODEV;
-        }
-    }
-    return 1;
-}
-
-static int __write_to_statsd_init(struct iovec* vec, size_t nr) {
-    int ret, save_errno = errno;
-
-    statsd_writer_init_lock();
-
-    if (__write_to_statsd == __write_to_statsd_init) {
-        ret = __write_to_statsd_initialize_locked();
-        if (ret < 0) {
-            statsd_writer_init_unlock();
-            errno = save_errno;
-            return ret;
-        }
-
-        __write_to_statsd = __write_to_stats_daemon;
-    }
-
-    statsd_writer_init_unlock();
-
-    ret = __write_to_statsd(vec, nr);
-    errno = save_errno;
-    return ret;
-}
diff --git a/libstats/socket/stats_event.c b/libstats/socket/stats_event.c
deleted file mode 100644
index f3e8087..0000000
--- a/libstats/socket/stats_event.c
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (C) 2019 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 "include/stats_event.h"
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include "stats_buffer_writer.h"
-
-#define LOGGER_ENTRY_MAX_PAYLOAD 4068
-// Max payload size is 4 bytes less as 4 bytes are reserved for stats_eventTag.
-// See android_util_Stats_Log.cpp
-#define MAX_PUSH_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - 4)
-
-#define MAX_PULL_EVENT_PAYLOAD (50 * 1024)  // 50 KB
-
-/* POSITIONS */
-#define POS_NUM_ELEMENTS 1
-#define POS_TIMESTAMP (POS_NUM_ELEMENTS + sizeof(uint8_t))
-#define POS_ATOM_ID (POS_TIMESTAMP + sizeof(uint8_t) + sizeof(uint64_t))
-
-/* LIMITS */
-#define MAX_ANNOTATION_COUNT 15
-#define MAX_BYTE_VALUE 127  // parsing side requires that lengths fit in 7 bits
-
-/* ERRORS */
-#define ERROR_NO_TIMESTAMP 0x1
-#define ERROR_NO_ATOM_ID 0x2
-#define ERROR_OVERFLOW 0x4
-#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
-#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
-#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
-#define ERROR_INVALID_ANNOTATION_ID 0x40
-#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
-#define ERROR_TOO_MANY_ANNOTATIONS 0x100
-#define ERROR_TOO_MANY_FIELDS 0x200
-#define ERROR_INVALID_VALUE_TYPE 0x400
-#define ERROR_STRING_NOT_NULL_TERMINATED 0x800
-#define ERROR_ATOM_ID_INVALID_POSITION 0x2000
-
-/* TYPE IDS */
-#define INT32_TYPE 0x00
-#define INT64_TYPE 0x01
-#define STRING_TYPE 0x02
-#define LIST_TYPE 0x03
-#define FLOAT_TYPE 0x04
-#define BOOL_TYPE 0x05
-#define BYTE_ARRAY_TYPE 0x06
-#define OBJECT_TYPE 0x07
-#define KEY_VALUE_PAIRS_TYPE 0x08
-#define ATTRIBUTION_CHAIN_TYPE 0x09
-#define ERROR_TYPE 0x0F
-
-// The AStatsEvent struct holds the serialized encoding of an event
-// within a buf. Also includes other required fields.
-struct AStatsEvent {
-    uint8_t* buf;
-    // Location of last field within the buf. Here, field denotes either a
-    // metadata field (e.g. timestamp) or an atom field.
-    size_t lastFieldPos;
-    // Number of valid bytes within the buffer.
-    size_t numBytesWritten;
-    uint32_t numElements;
-    uint32_t atomId;
-    uint32_t errors;
-    bool built;
-    size_t bufSize;
-};
-
-static int64_t get_elapsed_realtime_ns() {
-    struct timespec t;
-    t.tv_sec = t.tv_nsec = 0;
-    clock_gettime(CLOCK_BOOTTIME, &t);
-    return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
-}
-
-AStatsEvent* AStatsEvent_obtain() {
-    AStatsEvent* event = malloc(sizeof(AStatsEvent));
-    event->lastFieldPos = 0;
-    event->numBytesWritten = 2;  // reserve first 2 bytes for root event type and number of elements
-    event->numElements = 0;
-    event->atomId = 0;
-    event->errors = 0;
-    event->built = false;
-    event->bufSize = MAX_PUSH_EVENT_PAYLOAD;
-    event->buf = (uint8_t*)calloc(event->bufSize, 1);
-
-    event->buf[0] = OBJECT_TYPE;
-    AStatsEvent_writeInt64(event, get_elapsed_realtime_ns());  // write the timestamp
-
-    return event;
-}
-
-void AStatsEvent_release(AStatsEvent* event) {
-    free(event->buf);
-    free(event);
-}
-
-void AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId) {
-    if (event->atomId != 0) return;
-    if (event->numElements != 1) {
-        event->errors |= ERROR_ATOM_ID_INVALID_POSITION;
-        return;
-    }
-
-    event->atomId = atomId;
-    AStatsEvent_writeInt32(event, atomId);
-}
-
-// Overwrites the timestamp populated in AStatsEvent_obtain with a custom
-// timestamp. Should only be called from test code.
-void AStatsEvent_overwriteTimestamp(AStatsEvent* event, uint64_t timestampNs) {
-    memcpy(&event->buf[POS_TIMESTAMP + sizeof(uint8_t)], &timestampNs, sizeof(timestampNs));
-    // Do not increment numElements because we already accounted for the timestamp
-    // within AStatsEvent_obtain.
-}
-
-// Side-effect: modifies event->errors if the buffer would overflow
-static bool overflows(AStatsEvent* event, size_t size) {
-    const size_t totalBytesNeeded = event->numBytesWritten + size;
-    if (totalBytesNeeded > MAX_PULL_EVENT_PAYLOAD) {
-        event->errors |= ERROR_OVERFLOW;
-        return true;
-    }
-
-    // Expand buffer if needed.
-    if (event->bufSize < MAX_PULL_EVENT_PAYLOAD && totalBytesNeeded > event->bufSize) {
-        do {
-            event->bufSize *= 2;
-        } while (event->bufSize <= totalBytesNeeded);
-
-        if (event->bufSize > MAX_PULL_EVENT_PAYLOAD) {
-            event->bufSize = MAX_PULL_EVENT_PAYLOAD;
-        }
-
-        event->buf = (uint8_t*)realloc(event->buf, event->bufSize);
-    }
-    return false;
-}
-
-// Side-effect: all append functions increment event->numBytesWritten if there is
-// sufficient space within the buffer to place the value
-static void append_byte(AStatsEvent* event, uint8_t value) {
-    if (!overflows(event, sizeof(value))) {
-        event->buf[event->numBytesWritten] = value;
-        event->numBytesWritten += sizeof(value);
-    }
-}
-
-static void append_bool(AStatsEvent* event, bool value) {
-    append_byte(event, (uint8_t)value);
-}
-
-static void append_int32(AStatsEvent* event, int32_t value) {
-    if (!overflows(event, sizeof(value))) {
-        memcpy(&event->buf[event->numBytesWritten], &value, sizeof(value));
-        event->numBytesWritten += sizeof(value);
-    }
-}
-
-static void append_int64(AStatsEvent* event, int64_t value) {
-    if (!overflows(event, sizeof(value))) {
-        memcpy(&event->buf[event->numBytesWritten], &value, sizeof(value));
-        event->numBytesWritten += sizeof(value);
-    }
-}
-
-static void append_float(AStatsEvent* event, float value) {
-    if (!overflows(event, sizeof(value))) {
-        memcpy(&event->buf[event->numBytesWritten], &value, sizeof(value));
-        event->numBytesWritten += sizeof(float);
-    }
-}
-
-static void append_byte_array(AStatsEvent* event, const uint8_t* buf, size_t size) {
-    if (!overflows(event, size)) {
-        memcpy(&event->buf[event->numBytesWritten], buf, size);
-        event->numBytesWritten += size;
-    }
-}
-
-// Side-effect: modifies event->errors if buf is not properly null-terminated
-static void append_string(AStatsEvent* event, const char* buf) {
-    size_t size = strnlen(buf, MAX_PULL_EVENT_PAYLOAD);
-    if (size == MAX_PULL_EVENT_PAYLOAD) {
-        event->errors |= ERROR_STRING_NOT_NULL_TERMINATED;
-        return;
-    }
-
-    append_int32(event, size);
-    append_byte_array(event, (uint8_t*)buf, size);
-}
-
-static void start_field(AStatsEvent* event, uint8_t typeId) {
-    event->lastFieldPos = event->numBytesWritten;
-    append_byte(event, typeId);
-    event->numElements++;
-}
-
-void AStatsEvent_writeInt32(AStatsEvent* event, int32_t value) {
-    start_field(event, INT32_TYPE);
-    append_int32(event, value);
-}
-
-void AStatsEvent_writeInt64(AStatsEvent* event, int64_t value) {
-    start_field(event, INT64_TYPE);
-    append_int64(event, value);
-}
-
-void AStatsEvent_writeFloat(AStatsEvent* event, float value) {
-    start_field(event, FLOAT_TYPE);
-    append_float(event, value);
-}
-
-void AStatsEvent_writeBool(AStatsEvent* event, bool value) {
-    start_field(event, BOOL_TYPE);
-    append_bool(event, value);
-}
-
-void AStatsEvent_writeByteArray(AStatsEvent* event, const uint8_t* buf, size_t numBytes) {
-    start_field(event, BYTE_ARRAY_TYPE);
-    append_int32(event, numBytes);
-    append_byte_array(event, buf, numBytes);
-}
-
-// Value is assumed to be encoded using UTF8
-void AStatsEvent_writeString(AStatsEvent* event, const char* value) {
-    start_field(event, STRING_TYPE);
-    append_string(event, value);
-}
-
-// Tags are assumed to be encoded using UTF8
-void AStatsEvent_writeAttributionChain(AStatsEvent* event, const uint32_t* uids,
-                                       const char* const* tags, uint8_t numNodes) {
-    if (numNodes > MAX_BYTE_VALUE) {
-        event->errors |= ERROR_ATTRIBUTION_CHAIN_TOO_LONG;
-        return;
-    }
-
-    start_field(event, ATTRIBUTION_CHAIN_TYPE);
-    append_byte(event, numNodes);
-
-    for (uint8_t i = 0; i < numNodes; i++) {
-        append_int32(event, uids[i]);
-        append_string(event, tags[i]);
-    }
-}
-
-// Side-effect: modifies event->errors if field has too many annotations
-static void increment_annotation_count(AStatsEvent* event) {
-    uint8_t fieldType = event->buf[event->lastFieldPos] & 0x0F;
-    uint32_t oldAnnotationCount = (event->buf[event->lastFieldPos] & 0xF0) >> 4;
-    uint32_t newAnnotationCount = oldAnnotationCount + 1;
-
-    if (newAnnotationCount > MAX_ANNOTATION_COUNT) {
-        event->errors |= ERROR_TOO_MANY_ANNOTATIONS;
-        return;
-    }
-
-    event->buf[event->lastFieldPos] = (((uint8_t)newAnnotationCount << 4) & 0xF0) | fieldType;
-}
-
-void AStatsEvent_addBoolAnnotation(AStatsEvent* event, uint8_t annotationId, bool value) {
-    if (event->numElements < 2) {
-        event->errors |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
-        return;
-    } else if (annotationId > MAX_BYTE_VALUE) {
-        event->errors |= ERROR_ANNOTATION_ID_TOO_LARGE;
-        return;
-    }
-
-    append_byte(event, annotationId);
-    append_byte(event, BOOL_TYPE);
-    append_bool(event, value);
-    increment_annotation_count(event);
-}
-
-void AStatsEvent_addInt32Annotation(AStatsEvent* event, uint8_t annotationId, int32_t value) {
-    if (event->numElements < 2) {
-        event->errors |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
-        return;
-    } else if (annotationId > MAX_BYTE_VALUE) {
-        event->errors |= ERROR_ANNOTATION_ID_TOO_LARGE;
-        return;
-    }
-
-    append_byte(event, annotationId);
-    append_byte(event, INT32_TYPE);
-    append_int32(event, value);
-    increment_annotation_count(event);
-}
-
-uint32_t AStatsEvent_getAtomId(AStatsEvent* event) {
-    return event->atomId;
-}
-
-uint8_t* AStatsEvent_getBuffer(AStatsEvent* event, size_t* size) {
-    if (size) *size = event->numBytesWritten;
-    return event->buf;
-}
-
-uint32_t AStatsEvent_getErrors(AStatsEvent* event) {
-    return event->errors;
-}
-
-static void build_internal(AStatsEvent* event, const bool push) {
-    if (event->numElements > MAX_BYTE_VALUE) event->errors |= ERROR_TOO_MANY_FIELDS;
-    if (0 == event->atomId) event->errors |= ERROR_NO_ATOM_ID;
-    if (push && event->numBytesWritten > MAX_PUSH_EVENT_PAYLOAD) event->errors |= ERROR_OVERFLOW;
-
-    // If there are errors, rewrite buffer.
-    if (event->errors) {
-        // Discard everything after the atom id (including atom-level
-        // annotations). This leaves only two elements (timestamp and atom id).
-        event->numElements = 2;
-        // Reset number of atom-level annotations to 0.
-        event->buf[POS_ATOM_ID] = INT32_TYPE;
-        // Now, write errors to the buffer immediately after the atom id.
-        event->numBytesWritten = POS_ATOM_ID + sizeof(uint8_t) + sizeof(uint32_t);
-        start_field(event, ERROR_TYPE);
-        append_int32(event, event->errors);
-    }
-
-    event->buf[POS_NUM_ELEMENTS] = event->numElements;
-}
-
-void AStatsEvent_build(AStatsEvent* event) {
-    if (event->built) return;
-
-    build_internal(event, false /* push */);
-
-    event->built = true;
-}
-
-int AStatsEvent_write(AStatsEvent* event) {
-    build_internal(event, true /* push */);
-    return write_buffer_to_statsd(event->buf, event->numBytesWritten, event->atomId);
-}
diff --git a/libstats/socket/stats_socket.c b/libstats/socket/stats_socket.c
deleted file mode 100644
index 09f8967..0000000
--- a/libstats/socket/stats_socket.c
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2020 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 "include/stats_socket.h"
-#include "stats_buffer_writer.h"
-
-void AStatsSocket_close() {
-    stats_log_close();
-}
diff --git a/libstats/socket/statsd_writer.c b/libstats/socket/statsd_writer.c
deleted file mode 100644
index 73b7a7e..0000000
--- a/libstats/socket/statsd_writer.c
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Copyright (C) 2018, 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 "statsd_writer.h"
-
-#include <cutils/fs.h>
-#include <cutils/sockets.h>
-#include <cutils/threads.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <poll.h>
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-#include <stdarg.h>
-#include <stdatomic.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <sys/un.h>
-#include <time.h>
-#include <unistd.h>
-
-static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
-static atomic_int dropped = 0;
-static atomic_int log_error = 0;
-static atomic_int atom_tag = 0;
-
-void statsd_writer_init_lock() {
-    /*
-     * If we trigger a signal handler in the middle of locked activity and the
-     * signal handler logs a message, we could get into a deadlock state.
-     */
-    pthread_mutex_lock(&log_init_lock);
-}
-
-int statd_writer_trylock() {
-    return pthread_mutex_trylock(&log_init_lock);
-}
-
-void statsd_writer_init_unlock() {
-    pthread_mutex_unlock(&log_init_lock);
-}
-
-static int statsdAvailable();
-static int statsdOpen();
-static void statsdClose();
-static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
-static void statsdNoteDrop();
-static int statsdIsClosed();
-
-struct android_log_transport_write statsdLoggerWrite = {
-        .name = "statsd",
-        .sock = -EBADF,
-        .available = statsdAvailable,
-        .open = statsdOpen,
-        .close = statsdClose,
-        .write = statsdWrite,
-        .noteDrop = statsdNoteDrop,
-        .isClosed = statsdIsClosed,
-};
-
-/* log_init_lock assumed */
-static int statsdOpen() {
-    int i, ret = 0;
-
-    i = atomic_load(&statsdLoggerWrite.sock);
-    if (i < 0) {
-        int flags = SOCK_DGRAM;
-#ifdef SOCK_CLOEXEC
-        flags |= SOCK_CLOEXEC;
-#endif
-#ifdef SOCK_NONBLOCK
-        flags |= SOCK_NONBLOCK;
-#endif
-        int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, flags, 0));
-        if (sock < 0) {
-            ret = -errno;
-        } else {
-            int sndbuf = 1 * 1024 * 1024;  // set max send buffer size 1MB
-            socklen_t bufLen = sizeof(sndbuf);
-            // SO_RCVBUF does not have an effect on unix domain socket, but SO_SNDBUF does.
-            // Proceed to connect even setsockopt fails.
-            setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, bufLen);
-            struct sockaddr_un un;
-            memset(&un, 0, sizeof(struct sockaddr_un));
-            un.sun_family = AF_UNIX;
-            strcpy(un.sun_path, "/dev/socket/statsdw");
-
-            if (TEMP_FAILURE_RETRY(
-                        connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {
-                ret = -errno;
-                switch (ret) {
-                    case -ENOTCONN:
-                    case -ECONNREFUSED:
-                    case -ENOENT:
-                        i = atomic_exchange(&statsdLoggerWrite.sock, ret);
-                    /* FALLTHRU */
-                    default:
-                        break;
-                }
-                close(sock);
-            } else {
-                ret = atomic_exchange(&statsdLoggerWrite.sock, sock);
-                if ((ret >= 0) && (ret != sock)) {
-                    close(ret);
-                }
-                ret = 0;
-            }
-        }
-    }
-
-    return ret;
-}
-
-static void __statsdClose(int negative_errno) {
-    int sock = atomic_exchange(&statsdLoggerWrite.sock, negative_errno);
-    if (sock >= 0) {
-        close(sock);
-    }
-}
-
-static void statsdClose() {
-    __statsdClose(-EBADF);
-}
-
-static int statsdAvailable() {
-    if (atomic_load(&statsdLoggerWrite.sock) < 0) {
-        if (access("/dev/socket/statsdw", W_OK) == 0) {
-            return 0;
-        }
-        return -EBADF;
-    }
-    return 1;
-}
-
-static void statsdNoteDrop(int error, int tag) {
-    atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
-    atomic_exchange_explicit(&log_error, error, memory_order_relaxed);
-    atomic_exchange_explicit(&atom_tag, tag, memory_order_relaxed);
-}
-
-static int statsdIsClosed() {
-    if (atomic_load(&statsdLoggerWrite.sock) < 0) {
-        return 1;
-    }
-    return 0;
-}
-
-static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
-    ssize_t ret;
-    int sock;
-    static const unsigned headerLength = 1;
-    struct iovec newVec[nr + headerLength];
-    android_log_header_t header;
-    size_t i, payloadSize;
-
-    sock = atomic_load(&statsdLoggerWrite.sock);
-    if (sock < 0) switch (sock) {
-            case -ENOTCONN:
-            case -ECONNREFUSED:
-            case -ENOENT:
-                break;
-            default:
-                return -EBADF;
-        }
-    /*
-     *  struct {
-     *      // what we provide to socket
-     *      android_log_header_t header;
-     *      // caller provides
-     *      union {
-     *          struct {
-     *              char     prio;
-     *              char     payload[];
-     *          } string;
-     *          struct {
-     *              uint32_t tag
-     *              char     payload[];
-     *          } binary;
-     *      };
-     *  };
-     */
-
-    header.tid = gettid();
-    header.realtime.tv_sec = ts->tv_sec;
-    header.realtime.tv_nsec = ts->tv_nsec;
-
-    newVec[0].iov_base = (unsigned char*)&header;
-    newVec[0].iov_len = sizeof(header);
-
-    // If we dropped events before, try to tell statsd.
-    if (sock >= 0) {
-        int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
-        if (snapshot) {
-            android_log_event_long_t buffer;
-            header.id = LOG_ID_STATS;
-            // store the last log error in the tag field. This tag field is not used by statsd.
-            buffer.header.tag = atomic_load(&log_error);
-            buffer.payload.type = EVENT_TYPE_LONG;
-            // format:
-            // |atom_tag|dropped_count|
-            int64_t composed_long = atomic_load(&atom_tag);
-            // Send 2 int32's via an int64.
-            composed_long = ((composed_long << 32) | ((int64_t)snapshot));
-            buffer.payload.data = composed_long;
-
-            newVec[headerLength].iov_base = &buffer;
-            newVec[headerLength].iov_len = sizeof(buffer);
-
-            ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
-            if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
-                atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
-            }
-        }
-    }
-
-    header.id = LOG_ID_STATS;
-
-    for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
-        newVec[i].iov_base = vec[i - headerLength].iov_base;
-        payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
-
-        if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
-            newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
-            if (newVec[i].iov_len) {
-                ++i;
-            }
-            break;
-        }
-    }
-
-    /*
-     * The write below could be lost, but will never block.
-     *
-     * ENOTCONN occurs if statsd has died.
-     * ENOENT occurs if statsd is not running and socket is missing.
-     * ECONNREFUSED occurs if we can not reconnect to statsd.
-     * EAGAIN occurs if statsd is overloaded.
-     */
-    if (sock < 0) {
-        ret = sock;
-    } else {
-        ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i));
-        if (ret < 0) {
-            ret = -errno;
-        }
-    }
-    switch (ret) {
-        case -ENOTCONN:
-        case -ECONNREFUSED:
-        case -ENOENT:
-            if (statd_writer_trylock()) {
-                return ret; /* in a signal handler? try again when less stressed
-                             */
-            }
-            __statsdClose(ret);
-            ret = statsdOpen();
-            statsd_writer_init_unlock();
-
-            if (ret < 0) {
-                return ret;
-            }
-
-            ret = TEMP_FAILURE_RETRY(writev(atomic_load(&statsdLoggerWrite.sock), newVec, i));
-            if (ret < 0) {
-                ret = -errno;
-            }
-        /* FALLTHRU */
-        default:
-            break;
-    }
-
-    if (ret > (ssize_t)sizeof(header)) {
-        ret -= sizeof(header);
-    }
-
-    return ret;
-}
diff --git a/libstats/socket/statsd_writer.h b/libstats/socket/statsd_writer.h
deleted file mode 100644
index 562bda5..0000000
--- a/libstats/socket/statsd_writer.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_STATS_LOG_STATS_WRITER_H
-#define ANDROID_STATS_LOG_STATS_WRITER_H
-
-#include <pthread.h>
-#include <stdatomic.h>
-#include <sys/socket.h>
-
-/**
- * Internal lock should not be exposed. This is bad design.
- * TODO: rewrite it in c++ code and encapsulate the functionality in a
- * StatsdWriter class.
- */
-void statsd_writer_init_lock();
-int statsd_writer_init_trylock();
-void statsd_writer_init_unlock();
-
-struct android_log_transport_write {
-    const char* name; /* human name to describe the transport */
-    atomic_int sock;
-    int (*available)(); /* Does not cause resources to be taken */
-    int (*open)();      /* can be called multiple times, reusing current resources */
-    void (*close)();    /* free up resources */
-    /* write log to transport, returns number of bytes propagated, or -errno */
-    int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
-    /* note one log drop */
-    void (*noteDrop)(int error, int tag);
-    /* checks if the socket is closed */
-    int (*isClosed)();
-};
-
-#endif  // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libstats/socket/tests/stats_event_test.cpp b/libstats/socket/tests/stats_event_test.cpp
deleted file mode 100644
index 9a1fac8..0000000
--- a/libstats/socket/tests/stats_event_test.cpp
+++ /dev/null
@@ -1,460 +0,0 @@
-/*
- * Copyright (C) 2019 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 "stats_event.h"
-#include <gtest/gtest.h>
-#include <utils/SystemClock.h>
-
-// Keep in sync with stats_event.c. Consider moving to separate header file to avoid duplication.
-/* ERRORS */
-#define ERROR_NO_TIMESTAMP 0x1
-#define ERROR_NO_ATOM_ID 0x2
-#define ERROR_OVERFLOW 0x4
-#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
-#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
-#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
-#define ERROR_INVALID_ANNOTATION_ID 0x40
-#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
-#define ERROR_TOO_MANY_ANNOTATIONS 0x100
-#define ERROR_TOO_MANY_FIELDS 0x200
-#define ERROR_INVALID_VALUE_TYPE 0x400
-#define ERROR_STRING_NOT_NULL_TERMINATED 0x800
-#define ERROR_ATOM_ID_INVALID_POSITION 0x2000
-
-/* TYPE IDS */
-#define INT32_TYPE 0x00
-#define INT64_TYPE 0x01
-#define STRING_TYPE 0x02
-#define LIST_TYPE 0x03
-#define FLOAT_TYPE 0x04
-#define BOOL_TYPE 0x05
-#define BYTE_ARRAY_TYPE 0x06
-#define OBJECT_TYPE 0x07
-#define KEY_VALUE_PAIRS_TYPE 0x08
-#define ATTRIBUTION_CHAIN_TYPE 0x09
-#define ERROR_TYPE 0x0F
-
-using std::string;
-using std::vector;
-
-// Side-effect: this function moves the start of the buffer past the read value
-template <class T>
-T readNext(uint8_t** buffer) {
-    T value;
-    if ((reinterpret_cast<uintptr_t>(*buffer) % alignof(T)) == 0) {
-        value = *(T*)(*buffer);
-    } else {
-        memcpy(&value, *buffer, sizeof(T));
-    }
-    *buffer += sizeof(T);
-    return value;
-}
-
-void checkTypeHeader(uint8_t** buffer, uint8_t typeId, uint8_t numAnnotations = 0) {
-    uint8_t typeHeader = (numAnnotations << 4) | typeId;
-    EXPECT_EQ(readNext<uint8_t>(buffer), typeHeader);
-}
-
-template <class T>
-void checkScalar(uint8_t** buffer, T expectedValue) {
-    EXPECT_EQ(readNext<T>(buffer), expectedValue);
-}
-
-void checkString(uint8_t** buffer, const string& expectedString) {
-    uint32_t size = readNext<uint32_t>(buffer);
-    string parsedString((char*)(*buffer), size);
-    EXPECT_EQ(parsedString, expectedString);
-    *buffer += size;  // move buffer past string we just read
-}
-
-void checkByteArray(uint8_t** buffer, const vector<uint8_t>& expectedByteArray) {
-    uint32_t size = readNext<uint32_t>(buffer);
-    vector<uint8_t> parsedByteArray(*buffer, *buffer + size);
-    EXPECT_EQ(parsedByteArray, expectedByteArray);
-    *buffer += size;  // move buffer past byte array we just read
-}
-
-template <class T>
-void checkAnnotation(uint8_t** buffer, uint8_t annotationId, uint8_t typeId, T annotationValue) {
-    EXPECT_EQ(readNext<uint8_t>(buffer), annotationId);
-    EXPECT_EQ(readNext<uint8_t>(buffer), typeId);
-    checkScalar<T>(buffer, annotationValue);
-}
-
-void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int64_t endTime,
-                   uint32_t atomId, uint8_t numAtomLevelAnnotations = 0) {
-    // All events start with OBJECT_TYPE id.
-    checkTypeHeader(buffer, OBJECT_TYPE);
-
-    // We increment by 2 because the number of elements listed in the
-    // serialization accounts for the timestamp and atom id as well.
-    checkScalar(buffer, static_cast<uint8_t>(numElements + 2));
-
-    // Check timestamp
-    checkTypeHeader(buffer, INT64_TYPE);
-    int64_t timestamp = readNext<int64_t>(buffer);
-    EXPECT_GE(timestamp, startTime);
-    EXPECT_LE(timestamp, endTime);
-
-    // Check atom id
-    checkTypeHeader(buffer, INT32_TYPE, numAtomLevelAnnotations);
-    checkScalar(buffer, atomId);
-}
-
-TEST(StatsEventTest, TestScalars) {
-    uint32_t atomId = 100;
-    int32_t int32Value = -5;
-    int64_t int64Value = -2 * android::elapsedRealtimeNano();
-    float floatValue = 2.0;
-    bool boolValue = false;
-
-    int64_t startTime = android::elapsedRealtimeNano();
-    AStatsEvent* event = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(event, atomId);
-    AStatsEvent_writeInt32(event, int32Value);
-    AStatsEvent_writeInt64(event, int64Value);
-    AStatsEvent_writeFloat(event, floatValue);
-    AStatsEvent_writeBool(event, boolValue);
-    AStatsEvent_build(event);
-    int64_t endTime = android::elapsedRealtimeNano();
-
-    size_t bufferSize;
-    uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
-    uint8_t* bufferEnd = buffer + bufferSize;
-
-    checkMetadata(&buffer, /*numElements=*/4, startTime, endTime, atomId);
-
-    // check int32 element
-    checkTypeHeader(&buffer, INT32_TYPE);
-    checkScalar(&buffer, int32Value);
-
-    // check int64 element
-    checkTypeHeader(&buffer, INT64_TYPE);
-    checkScalar(&buffer, int64Value);
-
-    // check float element
-    checkTypeHeader(&buffer, FLOAT_TYPE);
-    checkScalar(&buffer, floatValue);
-
-    // check bool element
-    checkTypeHeader(&buffer, BOOL_TYPE);
-    checkScalar(&buffer, boolValue);
-
-    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
-    EXPECT_EQ(AStatsEvent_getErrors(event), 0);
-    AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestStrings) {
-    uint32_t atomId = 100;
-    string str = "test_string";
-
-    int64_t startTime = android::elapsedRealtimeNano();
-    AStatsEvent* event = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(event, atomId);
-    AStatsEvent_writeString(event, str.c_str());
-    AStatsEvent_build(event);
-    int64_t endTime = android::elapsedRealtimeNano();
-
-    size_t bufferSize;
-    uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
-    uint8_t* bufferEnd = buffer + bufferSize;
-
-    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
-
-    checkTypeHeader(&buffer, STRING_TYPE);
-    checkString(&buffer, str);
-
-    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
-    EXPECT_EQ(AStatsEvent_getErrors(event), 0);
-    AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestByteArrays) {
-    uint32_t atomId = 100;
-    vector<uint8_t> message = {'b', 'y', 't', '\0', 'e', 's'};
-
-    int64_t startTime = android::elapsedRealtimeNano();
-    AStatsEvent* event = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(event, atomId);
-    AStatsEvent_writeByteArray(event, message.data(), message.size());
-    AStatsEvent_build(event);
-    int64_t endTime = android::elapsedRealtimeNano();
-
-    size_t bufferSize;
-    uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
-    uint8_t* bufferEnd = buffer + bufferSize;
-
-    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
-
-    checkTypeHeader(&buffer, BYTE_ARRAY_TYPE);
-    checkByteArray(&buffer, message);
-
-    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
-    EXPECT_EQ(AStatsEvent_getErrors(event), 0);
-    AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestAttributionChains) {
-    uint32_t atomId = 100;
-
-    uint8_t numNodes = 50;
-    uint32_t uids[numNodes];
-    vector<string> tags(numNodes);  // storage that cTag elements point to
-    const char* cTags[numNodes];
-    for (int i = 0; i < (int)numNodes; i++) {
-        uids[i] = i;
-        tags.push_back("test" + std::to_string(i));
-        cTags[i] = tags[i].c_str();
-    }
-
-    int64_t startTime = android::elapsedRealtimeNano();
-    AStatsEvent* event = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(event, atomId);
-    AStatsEvent_writeAttributionChain(event, uids, cTags, numNodes);
-    AStatsEvent_build(event);
-    int64_t endTime = android::elapsedRealtimeNano();
-
-    size_t bufferSize;
-    uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
-    uint8_t* bufferEnd = buffer + bufferSize;
-
-    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
-
-    checkTypeHeader(&buffer, ATTRIBUTION_CHAIN_TYPE);
-    checkScalar(&buffer, numNodes);
-    for (int i = 0; i < numNodes; i++) {
-        checkScalar(&buffer, uids[i]);
-        checkString(&buffer, tags[i]);
-    }
-
-    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
-    EXPECT_EQ(AStatsEvent_getErrors(event), 0);
-    AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestFieldAnnotations) {
-    uint32_t atomId = 100;
-
-    // first element information
-    bool boolValue = false;
-    uint8_t boolAnnotation1Id = 1;
-    uint8_t boolAnnotation2Id = 2;
-    bool boolAnnotation1Value = true;
-    int32_t boolAnnotation2Value = 3;
-
-    // second element information
-    float floatValue = -5.0;
-    uint8_t floatAnnotation1Id = 3;
-    uint8_t floatAnnotation2Id = 4;
-    int32_t floatAnnotation1Value = 8;
-    bool floatAnnotation2Value = false;
-
-    int64_t startTime = android::elapsedRealtimeNano();
-    AStatsEvent* event = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(event, atomId);
-    AStatsEvent_writeBool(event, boolValue);
-    AStatsEvent_addBoolAnnotation(event, boolAnnotation1Id, boolAnnotation1Value);
-    AStatsEvent_addInt32Annotation(event, boolAnnotation2Id, boolAnnotation2Value);
-    AStatsEvent_writeFloat(event, floatValue);
-    AStatsEvent_addInt32Annotation(event, floatAnnotation1Id, floatAnnotation1Value);
-    AStatsEvent_addBoolAnnotation(event, floatAnnotation2Id, floatAnnotation2Value);
-    AStatsEvent_build(event);
-    int64_t endTime = android::elapsedRealtimeNano();
-
-    size_t bufferSize;
-    uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
-    uint8_t* bufferEnd = buffer + bufferSize;
-
-    checkMetadata(&buffer, /*numElements=*/2, startTime, endTime, atomId);
-
-    // check first element
-    checkTypeHeader(&buffer, BOOL_TYPE, /*numAnnotations=*/2);
-    checkScalar(&buffer, boolValue);
-    checkAnnotation(&buffer, boolAnnotation1Id, BOOL_TYPE, boolAnnotation1Value);
-    checkAnnotation(&buffer, boolAnnotation2Id, INT32_TYPE, boolAnnotation2Value);
-
-    // check second element
-    checkTypeHeader(&buffer, FLOAT_TYPE, /*numAnnotations=*/2);
-    checkScalar(&buffer, floatValue);
-    checkAnnotation(&buffer, floatAnnotation1Id, INT32_TYPE, floatAnnotation1Value);
-    checkAnnotation(&buffer, floatAnnotation2Id, BOOL_TYPE, floatAnnotation2Value);
-
-    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
-    EXPECT_EQ(AStatsEvent_getErrors(event), 0);
-    AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestAtomLevelAnnotations) {
-    uint32_t atomId = 100;
-    // atom-level annotation information
-    uint8_t boolAnnotationId = 1;
-    uint8_t int32AnnotationId = 2;
-    bool boolAnnotationValue = false;
-    int32_t int32AnnotationValue = 5;
-
-    float fieldValue = -3.5;
-
-    int64_t startTime = android::elapsedRealtimeNano();
-    AStatsEvent* event = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(event, atomId);
-    AStatsEvent_addBoolAnnotation(event, boolAnnotationId, boolAnnotationValue);
-    AStatsEvent_addInt32Annotation(event, int32AnnotationId, int32AnnotationValue);
-    AStatsEvent_writeFloat(event, fieldValue);
-    AStatsEvent_build(event);
-    int64_t endTime = android::elapsedRealtimeNano();
-
-    size_t bufferSize;
-    uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
-    uint8_t* bufferEnd = buffer + bufferSize;
-
-    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId,
-                  /*numAtomLevelAnnotations=*/2);
-
-    // check atom-level annotations
-    checkAnnotation(&buffer, boolAnnotationId, BOOL_TYPE, boolAnnotationValue);
-    checkAnnotation(&buffer, int32AnnotationId, INT32_TYPE, int32AnnotationValue);
-
-    // check first element
-    checkTypeHeader(&buffer, FLOAT_TYPE);
-    checkScalar(&buffer, fieldValue);
-
-    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
-    EXPECT_EQ(AStatsEvent_getErrors(event), 0);
-    AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestNoAtomIdError) {
-    AStatsEvent* event = AStatsEvent_obtain();
-    // Don't set the atom id in order to trigger the error.
-    AStatsEvent_build(event);
-
-    uint32_t errors = AStatsEvent_getErrors(event);
-    EXPECT_EQ(errors & ERROR_NO_ATOM_ID, ERROR_NO_ATOM_ID);
-
-    AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestPushOverflowError) {
-    const char* str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-    const int writeCount = 120;  // Number of times to write str in the event.
-
-    AStatsEvent* event = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(event, 100);
-
-    // Add str to the event 120 times. Each str takes >35 bytes so this will
-    // overflow the 4068 byte buffer.
-    // We want to keep writeCount less than 127 to avoid hitting
-    // ERROR_TOO_MANY_FIELDS.
-    for (int i = 0; i < writeCount; i++) {
-        AStatsEvent_writeString(event, str);
-    }
-    AStatsEvent_write(event);
-
-    uint32_t errors = AStatsEvent_getErrors(event);
-    EXPECT_EQ(errors & ERROR_OVERFLOW, ERROR_OVERFLOW);
-
-    AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestPullOverflowError) {
-    const uint32_t atomId = 10100;
-    const vector<uint8_t> bytes(430 /* number of elements */, 1 /* value of each element */);
-    const int writeCount = 120;  // Number of times to write bytes in the event.
-
-    AStatsEvent* event = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(event, atomId);
-
-    // Add bytes to the event 120 times. Size of bytes is 430 so this will
-    // overflow the 50 KB pulled event buffer.
-    // We want to keep writeCount less than 127 to avoid hitting
-    // ERROR_TOO_MANY_FIELDS.
-    for (int i = 0; i < writeCount; i++) {
-        AStatsEvent_writeByteArray(event, bytes.data(), bytes.size());
-    }
-    AStatsEvent_build(event);
-
-    uint32_t errors = AStatsEvent_getErrors(event);
-    EXPECT_EQ(errors & ERROR_OVERFLOW, ERROR_OVERFLOW);
-
-    AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestLargePull) {
-    const uint32_t atomId = 100;
-    const string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-    const int writeCount = 120;  // Number of times to write str in the event.
-    const int64_t startTime = android::elapsedRealtimeNano();
-
-    AStatsEvent* event = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(event, atomId);
-
-    // Add str to the event 120 times.
-    // We want to keep writeCount less than 127 to avoid hitting
-    // ERROR_TOO_MANY_FIELDS.
-    for (int i = 0; i < writeCount; i++) {
-        AStatsEvent_writeString(event, str.c_str());
-    }
-    AStatsEvent_build(event);
-    int64_t endTime = android::elapsedRealtimeNano();
-
-    size_t bufferSize;
-    uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
-    uint8_t* bufferEnd = buffer + bufferSize;
-
-    checkMetadata(&buffer, writeCount, startTime, endTime, atomId);
-
-    // Check all instances of str have been written.
-    for (int i = 0; i < writeCount; i++) {
-        checkTypeHeader(&buffer, STRING_TYPE);
-        checkString(&buffer, str);
-    }
-
-    EXPECT_EQ(buffer, bufferEnd);  // Ensure that we have read the entire buffer.
-    EXPECT_EQ(AStatsEvent_getErrors(event), 0);
-    AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestAtomIdInvalidPositionError) {
-    AStatsEvent* event = AStatsEvent_obtain();
-    AStatsEvent_writeInt32(event, 0);
-    AStatsEvent_setAtomId(event, 100);
-    AStatsEvent_writeBool(event, true);
-    AStatsEvent_build(event);
-
-    uint32_t errors = AStatsEvent_getErrors(event);
-    EXPECT_EQ(errors & ERROR_ATOM_ID_INVALID_POSITION, ERROR_ATOM_ID_INVALID_POSITION);
-
-    AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestOverwriteTimestamp) {
-    uint32_t atomId = 100;
-    int64_t expectedTimestamp = 0x123456789;
-    AStatsEvent* event = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(event, atomId);
-    AStatsEvent_overwriteTimestamp(event, expectedTimestamp);
-    AStatsEvent_build(event);
-
-    uint8_t* buffer = AStatsEvent_getBuffer(event, NULL);
-
-    // Make sure that the timestamp is being overwritten.
-    checkMetadata(&buffer, /*numElements=*/0, /*startTime=*/expectedTimestamp,
-                  /*endTime=*/expectedTimestamp, atomId);
-
-    EXPECT_EQ(AStatsEvent_getErrors(event), 0);
-    AStatsEvent_release(event);
-}
diff --git a/libstats/socket/tests/stats_writer_test.cpp b/libstats/socket/tests/stats_writer_test.cpp
deleted file mode 100644
index 749599f..0000000
--- a/libstats/socket/tests/stats_writer_test.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2020 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 <gtest/gtest.h>
-#include "stats_buffer_writer.h"
-#include "stats_event.h"
-#include "stats_socket.h"
-
-TEST(StatsWriterTest, TestSocketClose) {
-    AStatsEvent* event = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(event, 100);
-    AStatsEvent_writeInt32(event, 5);
-    int successResult = AStatsEvent_write(event);
-    AStatsEvent_release(event);
-
-    // In the case of a successful write, we return the number of bytes written.
-    EXPECT_GT(successResult, 0);
-    EXPECT_FALSE(stats_log_is_closed());
-
-    AStatsSocket_close();
-
-    EXPECT_TRUE(stats_log_is_closed());
-}
diff --git a/libsync/include/ndk/sync.h b/libsync/include/ndk/sync.h
index 2a59e35..38ccb68 100644
--- a/libsync/include/ndk/sync.h
+++ b/libsync/include/ndk/sync.h
@@ -33,8 +33,6 @@
 
 __BEGIN_DECLS
 
-#if __ANDROID_API__ >= 26
-
 /* Fences indicate the status of an asynchronous task. They are initially
  * in unsignaled state (0), and make a one-time transition to either signaled
  * (1) or error (< 0) state. A sync file is a collection of one or more fences;
@@ -101,8 +99,6 @@
  */
 void sync_file_info_free(struct sync_file_info* info) __INTRODUCED_IN(26);
 
-#endif /* __ANDROID_API__ >= 26 */
-
 __END_DECLS
 
 #endif /* ANDROID_SYNC_H */
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index 12c946c..b37b8ec 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -1,6 +1,7 @@
 cc_library_headers {
     name: "libsystem_headers",
     vendor_available: true,
+    product_available: true,
     recovery_available: true,
     vendor_ramdisk_available: true,
     host_supported: true,
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 8ee16f3..1e7cbdb 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -15,6 +15,7 @@
 cc_library_headers {
     name: "libutils_headers",
     vendor_available: true,
+    product_available: true,
     recovery_available: true,
     vendor_ramdisk_available: true,
     host_supported: true,
@@ -62,6 +63,7 @@
 cc_defaults {
     name: "libutils_defaults",
     vendor_available: true,
+    product_available: true,
     recovery_available: true,
     vndk: {
         enabled: true,
diff --git a/libutils/RefBase_fuzz.cpp b/libutils/RefBase_fuzz.cpp
old mode 100755
new mode 100644
index 2a92531..69288b3
--- a/libutils/RefBase_fuzz.cpp
+++ b/libutils/RefBase_fuzz.cpp
@@ -14,66 +14,156 @@
  * limitations under the License.
  */
 
-#include <atomic>
+#define LOG_TAG "RefBaseFuzz"
+
 #include <thread>
 
 #include "fuzzer/FuzzedDataProvider.h"
+#include "utils/Log.h"
+#include "utils/RWLock.h"
 #include "utils/RefBase.h"
 #include "utils/StrongPointer.h"
+
 using android::RefBase;
+using android::RWLock;
 using android::sp;
 using android::wp;
 
-static constexpr int REFBASE_INITIAL_STRONG_VALUE = (1 << 28);
-static constexpr int REFBASE_MAX_COUNT = 0xfffff;
-
-static constexpr int MAX_OPERATIONS = 100;
-static constexpr int MAX_THREADS = 10;
-
-bool canDecrementStrong(RefBase* ref) {
-    // There's an assert around decrementing the strong count too much that causes an artificial
-    // crash This is just running BAD_STRONG from RefBase
-    const int32_t count = ref->getStrongCount() - 1;
-    return !(count == 0 || ((count) & (~(REFBASE_MAX_COUNT | REFBASE_INITIAL_STRONG_VALUE))) != 0);
-}
-bool canDecrementWeak(RefBase* ref) {
-    const int32_t count = ref->getWeakRefs()->getWeakCount() - 1;
-    return !((count) == 0 || ((count) & (~REFBASE_MAX_COUNT)) != 0);
-}
-
+static constexpr int kMaxOperations = 100;
+static constexpr int kMaxThreads = 10;
 struct RefBaseSubclass : public RefBase {
-    RefBaseSubclass() {}
-    virtual ~RefBaseSubclass() {}
+  public:
+    RefBaseSubclass(bool* deletedCheck, RWLock& deletedMtx)
+        : mDeleted(deletedCheck), mRwLock(deletedMtx) {
+        RWLock::AutoWLock lock(mRwLock);
+        *mDeleted = false;
+        extendObjectLifetime(OBJECT_LIFETIME_WEAK);
+    }
+
+    virtual ~RefBaseSubclass() {
+        RWLock::AutoWLock lock(mRwLock);
+        *mDeleted = true;
+    }
+
+  private:
+    bool* mDeleted;
+    android::RWLock& mRwLock;
 };
 
-std::vector<std::function<void(RefBaseSubclass*)>> operations = {
-        [](RefBaseSubclass* ref) -> void { ref->getStrongCount(); },
-        [](RefBaseSubclass* ref) -> void { ref->printRefs(); },
-        [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->printRefs(); },
-        [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->getWeakCount(); },
-        [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->refBase(); },
-        [](RefBaseSubclass* ref) -> void { ref->incStrong(nullptr); },
-        [](RefBaseSubclass* ref) -> void {
-            if (canDecrementStrong(ref)) {
+// A thread-specific state object for ref
+struct RefThreadState {
+    size_t strongCount = 0;
+    size_t weakCount = 0;
+};
+
+RWLock gRefDeletedLock;
+bool gRefDeleted = false;
+bool gHasModifiedRefs = false;
+RefBaseSubclass* ref;
+RefBase::weakref_type* weakRefs;
+
+// These operations don't need locks as they explicitly check per-thread counts before running
+// they also have the potential to write to gRefDeleted, so must not be locked.
+const std::vector<std::function<void(RefThreadState*)>> kUnlockedOperations = {
+        [](RefThreadState* refState) -> void {
+            if (refState->strongCount > 0) {
                 ref->decStrong(nullptr);
+                gHasModifiedRefs = true;
+                refState->strongCount--;
             }
         },
-        [](RefBaseSubclass* ref) -> void { ref->forceIncStrong(nullptr); },
-        [](RefBaseSubclass* ref) -> void { ref->createWeak(nullptr); },
-        [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->attemptIncStrong(nullptr); },
-        [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->attemptIncWeak(nullptr); },
-        [](RefBaseSubclass* ref) -> void {
-            if (canDecrementWeak(ref)) {
-                ref->getWeakRefs()->decWeak(nullptr);
+        [](RefThreadState* refState) -> void {
+            if (refState->weakCount > 0) {
+                weakRefs->decWeak(nullptr);
+                gHasModifiedRefs = true;
+                refState->weakCount--;
             }
         },
-        [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->incWeak(nullptr); },
-        [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->printRefs(); },
 };
 
-void loop(RefBaseSubclass* loopRef, const std::vector<uint8_t>& fuzzOps) {
+const std::vector<std::function<void(RefThreadState*)>> kMaybeLockedOperations = {
+        // Read-only operations
+        [](RefThreadState*) -> void { ref->getStrongCount(); },
+        [](RefThreadState*) -> void { weakRefs->getWeakCount(); },
+        [](RefThreadState*) -> void { ref->printRefs(); },
+
+        // Read/write operations
+        [](RefThreadState* refState) -> void {
+            ref->incStrong(nullptr);
+            gHasModifiedRefs = true;
+            refState->strongCount++;
+        },
+        [](RefThreadState* refState) -> void {
+            ref->forceIncStrong(nullptr);
+            gHasModifiedRefs = true;
+            refState->strongCount++;
+        },
+        [](RefThreadState* refState) -> void {
+            ref->createWeak(nullptr);
+            gHasModifiedRefs = true;
+            refState->weakCount++;
+        },
+        [](RefThreadState* refState) -> void {
+            // This will increment weak internally, then attempt to
+            // promote it to strong. If it fails, it decrements weak.
+            // If it succeeds, the weak is converted to strong.
+            // Both cases net no weak reference change.
+            if (weakRefs->attemptIncStrong(nullptr)) {
+                refState->strongCount++;
+                gHasModifiedRefs = true;
+            }
+        },
+        [](RefThreadState* refState) -> void {
+            if (weakRefs->attemptIncWeak(nullptr)) {
+                refState->weakCount++;
+                gHasModifiedRefs = true;
+            }
+        },
+        [](RefThreadState* refState) -> void {
+            weakRefs->incWeak(nullptr);
+            gHasModifiedRefs = true;
+            refState->weakCount++;
+        },
+};
+
+void loop(const std::vector<uint8_t>& fuzzOps) {
+    RefThreadState state;
+    uint8_t lockedOpSize = kMaybeLockedOperations.size();
+    uint8_t totalOperationTypes = lockedOpSize + kUnlockedOperations.size();
     for (auto op : fuzzOps) {
-        operations[op % operations.size()](loopRef);
+        auto opVal = op % totalOperationTypes;
+        if (opVal >= lockedOpSize) {
+            kUnlockedOperations[opVal % lockedOpSize](&state);
+        } else {
+            // We only need to lock if we have no strong or weak count
+            bool shouldLock = state.strongCount == 0 && state.weakCount == 0;
+            if (shouldLock) {
+                gRefDeletedLock.readLock();
+                // If ref has deleted itself, we can no longer fuzz on this thread.
+                if (gRefDeleted) {
+                    // Unlock since we're exiting the loop here.
+                    gRefDeletedLock.unlock();
+                    return;
+                }
+            }
+            // Execute the locked operation
+            kMaybeLockedOperations[opVal](&state);
+            // Unlock if we locked.
+            if (shouldLock) {
+                gRefDeletedLock.unlock();
+            }
+        }
+    }
+
+    // Instead of explicitly freeing this, we're going to remove our weak and
+    // strong references.
+    for (; state.weakCount > 0; state.weakCount--) {
+        weakRefs->decWeak(nullptr);
+    }
+
+    // Clean up any strong references
+    for (; state.strongCount > 0; state.strongCount--) {
+        ref->decStrong(nullptr);
     }
 }
 
@@ -81,23 +171,35 @@
     std::vector<std::thread> threads = std::vector<std::thread>();
 
     // Get the number of threads to generate
-    uint8_t count = dataProvider->ConsumeIntegralInRange<uint8_t>(1, MAX_THREADS);
-
+    uint8_t count = dataProvider->ConsumeIntegralInRange<uint8_t>(1, kMaxThreads);
     // Generate threads
     for (uint8_t i = 0; i < count; i++) {
-        RefBaseSubclass* threadRef = new RefBaseSubclass();
-        uint8_t opCount = dataProvider->ConsumeIntegralInRange<uint8_t>(1, MAX_OPERATIONS);
+        uint8_t opCount = dataProvider->ConsumeIntegralInRange<uint8_t>(1, kMaxOperations);
         std::vector<uint8_t> threadOperations = dataProvider->ConsumeBytes<uint8_t>(opCount);
-        std::thread tmp = std::thread(loop, threadRef, threadOperations);
-        threads.push_back(move(tmp));
+        std::thread tmpThread = std::thread(loop, threadOperations);
+        threads.push_back(move(tmpThread));
     }
 
     for (auto& th : threads) {
         th.join();
     }
 }
+
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    gHasModifiedRefs = false;
+    ref = new RefBaseSubclass(&gRefDeleted, gRefDeletedLock);
+    weakRefs = ref->getWeakRefs();
+    // Since we are modifying flags, (flags & OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK
+    // is true. The destructor for RefBase should clean up weakrefs because of this.
     FuzzedDataProvider dataProvider(data, size);
     spawnThreads(&dataProvider);
+    LOG_ALWAYS_FATAL_IF(!gHasModifiedRefs && gRefDeleted, "ref(%p) was prematurely deleted!", ref);
+    // We need to explicitly delete this object
+    // if no refs have been added or deleted.
+    if (!gHasModifiedRefs && !gRefDeleted) {
+        delete ref;
+    }
+    LOG_ALWAYS_FATAL_IF(gHasModifiedRefs && !gRefDeleted,
+                        "ref(%p) should be deleted, is it leaking?", ref);
     return 0;
 }
diff --git a/property_service/OWNERS b/property_service/OWNERS
index babbe4d..7529cb9 100644
--- a/property_service/OWNERS
+++ b/property_service/OWNERS
@@ -1 +1 @@
-tomcherry@google.com
+include platform/system/core:/janitors/OWNERS
diff --git a/qemu_pipe/OWNERS b/qemu_pipe/OWNERS
index dbc1bf6..d67a329 100644
--- a/qemu_pipe/OWNERS
+++ b/qemu_pipe/OWNERS
@@ -1 +1,3 @@
 bohu@google.com
+lfy@google.com
+rkir@google.com
diff --git a/rootdir/Android.bp b/rootdir/Android.bp
index a21f686..d63868a 100644
--- a/rootdir/Android.bp
+++ b/rootdir/Android.bp
@@ -29,4 +29,5 @@
 linker_config {
     name: "system_linker_config",
     src: "etc/linker.config.json",
+    installable: false,
 }
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index c4c8768..73d1101 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -78,7 +78,7 @@
 # create some directories (some are mount points) and symlinks
 LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
     dev proc sys system data data_mirror odm oem acct config storage mnt apex debug_ramdisk \
-    linkerconfig second_stage_resources $(BOARD_ROOT_EXTRA_FOLDERS)); \
+    linkerconfig second_stage_resources postinstall $(BOARD_ROOT_EXTRA_FOLDERS)); \
     ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
     ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
     ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
@@ -153,11 +153,6 @@
     ; mkdir -p $(dir $(TARGET_ROOT_OUT)/$(word 2,$(p))) \
     ; ln -sf $(word 1,$(p)) $(TARGET_ROOT_OUT)/$(word 2,$(p)))
 endif
-# The A/B updater uses a top-level /postinstall directory to mount the new
-# system before reboot.
-ifeq ($(AB_OTA_UPDATER),true)
-  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/postinstall
-endif
 
 # The init symlink must be a post install command of a file that is to TARGET_ROOT_OUT.
 # Since init.environ.rc is required for init and satisfies that requirement, we hijack it to create the symlink.
diff --git a/rootdir/OWNERS b/rootdir/OWNERS
index ca22eb8..5d0d673 100644
--- a/rootdir/OWNERS
+++ b/rootdir/OWNERS
@@ -1,5 +1,7 @@
+bowgotsai@google.com
 ccross@google.com
+dvander@google.com
+elsk@google.com
 jeffv@google.com
 jiyong@google.com
 smoreland@google.com
-tomcherry@google.com
diff --git a/rootdir/avb/Android.bp b/rootdir/avb/Android.bp
index 85d2786..8c7caf3 100644
--- a/rootdir/avb/Android.bp
+++ b/rootdir/avb/Android.bp
@@ -18,3 +18,10 @@
         "s-gsi.avbpubkey",
     ],
 }
+
+filegroup {
+    name: "qcar-gsi_avbpubkey",
+    srcs: [
+        "qcar-gsi.avbpubkey",
+    ],
+}
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
index c8fc1d6..f71f205 100644
--- a/rootdir/avb/Android.mk
+++ b/rootdir/avb/Android.mk
@@ -80,4 +80,15 @@
 
 include $(BUILD_PREBUILT)
 
+#######################################
+# qcar-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := qcar-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
+
+include $(BUILD_PREBUILT)
+
 my_gsi_avb_keys_path :=
diff --git a/rootdir/avb/qcar-gsi.avbpubkey b/rootdir/avb/qcar-gsi.avbpubkey
new file mode 100644
index 0000000..ce56646
--- /dev/null
+++ b/rootdir/avb/qcar-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index d66ab73..2faf608 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -1,47 +1,4 @@
 {
-  // These are list of libraries which has stub interface and installed
-  // in system image so other partition and APEX modules can link to it.
-  // TODO(b/147210213) : Generate this list on build and read from the file
-  "provideLibs": [
-    // LLNDK libraries
-    "libEGL.so",
-    "libGLESv1_CM.so",
-    "libGLESv2.so",
-    "libGLESv3.so",
-    "libRS.so",
-    "libandroid_net.so",
-    "libbinder_ndk.so",
-    "libc.so",
-    "libcgrouprc.so",
-    "libclang_rt.asan-arm-android.so",
-    "libclang_rt.asan-i686-android.so",
-    "libclang_rt.asan-x86_64-android.so",
-    "libdl.so",
-    "libft2.so",
-    "liblog.so",
-    "libm.so",
-    "libmediandk.so",
-    "libnativewindow.so",
-    "libsync.so",
-    "libvndksupport.so",
-    "libvulkan.so",
-    // NDK libraries
-    "libaaudio.so",
-    "libandroid.so",
-    // adb
-    "libadbd_auth.so",
-    "libadbd_fs.so",
-    // bionic
-    "libdl_android.so",
-    // statsd
-    "libincident.so",
-    // media
-    "libmediametrics.so",
-    // nn
-    "libneuralnetworks_packageinfo.so",
-    // SELinux
-    "libselinux.so"
-  ],
   "requireLibs": [
     // Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
     "libdexfile_external.so",
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 67d91fc..21a78c1 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -56,7 +56,7 @@
     write /sys/module/dm_verity/parameters/prefetch_cluster 0
 
     # Generate ld.config.txt for early executed processes
-    exec -- /system/bin/linkerconfig --target /linkerconfig/bootstrap
+    exec -- /system/bin/bootstrap/linkerconfig --target /linkerconfig/bootstrap
     chmod 644 /linkerconfig/bootstrap/ld.config.txt
     copy /linkerconfig/bootstrap/ld.config.txt /linkerconfig/default/ld.config.txt
     chmod 644 /linkerconfig/default/ld.config.txt
@@ -153,21 +153,76 @@
     mkdir /dev/cpuctl/background
     mkdir /dev/cpuctl/top-app
     mkdir /dev/cpuctl/rt
+    mkdir /dev/cpuctl/system
+    mkdir /dev/cpuctl/system-background
     chown system system /dev/cpuctl
     chown system system /dev/cpuctl/foreground
     chown system system /dev/cpuctl/background
     chown system system /dev/cpuctl/top-app
     chown system system /dev/cpuctl/rt
+    chown system system /dev/cpuctl/system
+    chown system system /dev/cpuctl/system-background
     chown system system /dev/cpuctl/tasks
     chown system system /dev/cpuctl/foreground/tasks
     chown system system /dev/cpuctl/background/tasks
     chown system system /dev/cpuctl/top-app/tasks
     chown system system /dev/cpuctl/rt/tasks
+    chown system system /dev/cpuctl/system/tasks
+    chown system system /dev/cpuctl/system-background/tasks
     chmod 0664 /dev/cpuctl/tasks
     chmod 0664 /dev/cpuctl/foreground/tasks
     chmod 0664 /dev/cpuctl/background/tasks
     chmod 0664 /dev/cpuctl/top-app/tasks
     chmod 0664 /dev/cpuctl/rt/tasks
+    chmod 0664 /dev/cpuctl/system/tasks
+    chmod 0664 /dev/cpuctl/system-background/tasks
+
+    # Create a cpu group for NNAPI HAL processes
+    mkdir /dev/cpuctl/nnapi-hal
+    chown system system /dev/cpuctl/nnapi-hal
+    chown system system /dev/cpuctl/nnapi-hal/tasks
+    chmod 0664 /dev/cpuctl/nnapi-hal/tasks
+    write /dev/cpuctl/nnapi-hal/cpu.uclamp.min 1
+    write /dev/cpuctl/nnapi-hal/cpu.uclamp.latency_sensitive 1
+
+    # Create a cpu group for camera daemon processes
+    mkdir /dev/cpuctl/camera-daemon
+    chown system system /dev/cpuctl/camera-daemon
+    chown system system /dev/cpuctl/camera-daemon/tasks
+    chmod 0664 /dev/cpuctl/camera-daemon/tasks
+
+    # Android only use global RT throttling and doesn't use CONFIG_RT_GROUP_SCHED
+    # for RT group throttling. These values here are just to make sure RT threads
+    # can be migrated to those groups. These settings can be removed once we migrate
+    # to GKI kernel.
+    write /dev/cpuctl/cpu.rt_period_us 1000000
+    write /dev/cpuctl/cpu.rt_runtime_us 950000
+    # Surfaceflinger is in FG group so giving it a bit more
+    write /dev/cpuctl/foreground/cpu.rt_runtime_us 450000
+    write /dev/cpuctl/foreground/cpu.rt_period_us 1000000
+    write /dev/cpuctl/background/cpu.rt_runtime_us 50000
+    write /dev/cpuctl/background/cpu.rt_period_us 1000000
+    write /dev/cpuctl/top-app/cpu.rt_runtime_us 100000
+    write /dev/cpuctl/top-app/cpu.rt_period_us 1000000
+    write /dev/cpuctl/rt/cpu.rt_runtime_us 100000
+    write /dev/cpuctl/rt/cpu.rt_period_us 1000000
+    write /dev/cpuctl/system/cpu.rt_runtime_us 100000
+    write /dev/cpuctl/system/cpu.rt_period_us 1000000
+    write /dev/cpuctl/system-background/cpu.rt_runtime_us 50000
+    write /dev/cpuctl/system-background/cpu.rt_period_us 1000000
+    write /dev/cpuctl/nnapi-hal/cpu.rt_runtime_us 50000
+    write /dev/cpuctl/nnapi-hal/cpu.rt_period_us 1000000
+    write /dev/cpuctl/camera-daemon/cpu.rt_runtime_us 50000
+    write /dev/cpuctl/camera-daemon/cpu.rt_period_us 1000000
+
+    # Migrate root group to system subgroup
+    copy_per_line /dev/cpuctl/tasks /dev/cpuctl/system/tasks
+
+    # Create an stune group for camera-specific processes
+    mkdir /dev/stune/camera-daemon
+    chown system system /dev/stune/camera-daemon
+    chown system system /dev/stune/camera-daemon/tasks
+    chmod 0664 /dev/stune/camera-daemon/tasks
 
     # Create an stune group for NNAPI HAL processes
     mkdir /dev/stune/nnapi-hal
@@ -177,14 +232,6 @@
     write /dev/stune/nnapi-hal/schedtune.boost 1
     write /dev/stune/nnapi-hal/schedtune.prefer_idle 1
 
-    # cpuctl hierarchy for devices using utilclamp
-    mkdir /dev/cpuctl/nnapi-hal
-    chown system system /dev/cpuctl/nnapi-hal
-    chown system system /dev/cpuctl/nnapi-hal/tasks
-    chmod 0664 /dev/cpuctl/nnapi-hal/tasks
-    write /dev/cpuctl/nnapi-hal/cpu.uclamp.min 1
-    write /dev/cpuctl/nnapi-hal/cpu.uclamp.latency_sensitive 1
-
     # Create blkio group and apply initial settings.
     # This feature needs kernel to support it, and the
     # device's init.rc must actually set the correct values.
@@ -275,8 +322,6 @@
     write /proc/sys/vm/mmap_min_addr 32768
     write /proc/sys/net/ipv4/ping_group_range "0 2147483647"
     write /proc/sys/net/unix/max_dgram_qlen 600
-    write /proc/sys/kernel/sched_rt_runtime_us 950000
-    write /proc/sys/kernel/sched_rt_period_us 1000000
 
     # Assign reasonable ceiling values for socket rcv/snd buffers.
     # These should almost always be overridden by the target per the
@@ -298,13 +343,6 @@
     # /proc/net/fib_trie leaks interface IP addresses
     chmod 0400 /proc/net/fib_trie
 
-    # Create cgroup mount points for process groups
-    chown system system /dev/cpuctl
-    chown system system /dev/cpuctl/tasks
-    chmod 0666 /dev/cpuctl/tasks
-    write /dev/cpuctl/cpu.rt_period_us 1000000
-    write /dev/cpuctl/cpu.rt_runtime_us 950000
-
     # sets up initial cpusets for ActivityManager
     # this ensures that the cpusets are present and usable, but the device's
     # init.rc must actually set the correct cpus
@@ -317,7 +355,6 @@
 
     # system-background is for system tasks that should only run on
     # little cores, not on bigs
-    # to be used only by init, so don't change system-bg permissions
     mkdir /dev/cpuset/system-background
     copy /dev/cpuset/cpus /dev/cpuset/system-background/cpus
     copy /dev/cpuset/mems /dev/cpuset/system-background/mems
@@ -332,6 +369,11 @@
     copy /dev/cpuset/cpus /dev/cpuset/top-app/cpus
     copy /dev/cpuset/mems /dev/cpuset/top-app/mems
 
+    # create a cpuset for camera daemon processes
+    mkdir /dev/cpuset/camera-daemon
+    copy /dev/cpuset/cpus /dev/cpuset/camera-daemon/cpus
+    copy /dev/cpuset/mems /dev/cpuset/camera-daemon/mems
+
     # change permissions for all cpusets we'll touch at runtime
     chown system system /dev/cpuset
     chown system system /dev/cpuset/foreground
@@ -339,12 +381,14 @@
     chown system system /dev/cpuset/system-background
     chown system system /dev/cpuset/top-app
     chown system system /dev/cpuset/restricted
+    chown system system /dev/cpuset/camera-daemon
     chown system system /dev/cpuset/tasks
     chown system system /dev/cpuset/foreground/tasks
     chown system system /dev/cpuset/background/tasks
     chown system system /dev/cpuset/system-background/tasks
     chown system system /dev/cpuset/top-app/tasks
     chown system system /dev/cpuset/restricted/tasks
+    chown system system /dev/cpuset/camera-daemon/tasks
 
     # set system-background to 0775 so SurfaceFlinger can touch it
     chmod 0775 /dev/cpuset/system-background
@@ -355,6 +399,7 @@
     chmod 0664 /dev/cpuset/top-app/tasks
     chmod 0664 /dev/cpuset/restricted/tasks
     chmod 0664 /dev/cpuset/tasks
+    chmod 0664 /dev/cpuset/camera-daemon/tasks
 
     # make the PSI monitor accessible to others
     chown system system /proc/pressure/memory
@@ -547,6 +592,7 @@
     mkdir /metadata/ota 0700 root system
     mkdir /metadata/ota/snapshots 0700 root system
     mkdir /metadata/userspacereboot 0770 root system
+    mkdir /metadata/watchdog 0770 root system
 
     mkdir /metadata/apex 0700 root system
     mkdir /metadata/apex/sessions 0700 root system
@@ -567,6 +613,9 @@
     # HALs required before storage encryption can get unlocked (FBE/FDE)
     class_start early_hal
 
+    # Load trusted keys from dm-verity protected partitions
+    exec -- /system/bin/fsverity_init --load-verified-keys
+
 on post-fs-data
     mark_post_data
 
@@ -587,27 +636,28 @@
     mkdir /data/bootchart 0755 shell shell encryption=Require
     bootchart start
 
-    # Make sure that apexd is started in the default namespace
-    enter_default_mount_ns
-
     mkdir /data/vendor 0771 root root encryption=Require
     mkdir /data/vendor_ce 0771 root root encryption=None
     mkdir /data/vendor_de 0771 root root encryption=None
     mkdir /data/vendor/hardware 0771 root root
 
     # Start tombstoned early to be able to store tombstones.
+    mkdir /data/anr 0775 system system encryption=Require
     mkdir /data/tombstones 0771 system system encryption=Require
     mkdir /data/vendor/tombstones 0771 root root
     mkdir /data/vendor/tombstones/wifi 0771 wifi wifi
     start tombstoned
 
+    # Make sure that apexd is started in the default namespace
+    enter_default_mount_ns
+
     # /data/apex is now available. Start apexd to scan and activate APEXes.
     mkdir /data/apex 0755 root system encryption=None
     mkdir /data/apex/active 0755 root system
     mkdir /data/apex/backup 0700 root system
     mkdir /data/apex/hashtree 0700 root system
     mkdir /data/apex/sessions 0700 root system
-    mkdir /data/app-staging 0750 system system encryption=DeleteIfNecessary
+    mkdir /data/app-staging 0751 system system encryption=DeleteIfNecessary
     start apexd
 
     # Avoid predictable entropy pool. Carry over entropy from previous boot.
@@ -738,8 +788,6 @@
     # the following directory.
     mkdir /data/mediadrm 0770 mediadrm mediadrm encryption=Require
 
-    mkdir /data/anr 0775 system system encryption=Require
-
     # NFC: create data/nfc for nv storage
     mkdir /data/nfc 0770 nfc nfc encryption=Require
     mkdir /data/nfc/param 0770 nfc nfc
@@ -822,15 +870,22 @@
 
     init_user0
 
+    # Set SELinux security contexts on upgrade or policy update.
+    restorecon --recursive --skip-ce /data
+
+    # After apexes are mounted, tell keymaster early boot has ended, so it will
+    # stop allowing use of early-boot keys
+    exec - system system -- /system/bin/vdc keymaster earlyBootEnded
+
+    # Lock the fs-verity keyring, so no more keys can be added
+    exec -- /system/bin/fsverity_init --lock
+
     # Allow apexd to snapshot and restore device encrypted apex data in the case
     # of a rollback. This should be done immediately after DE_user data keys
     # are loaded. APEXes should not access this data until this has been
     # completed and apexd.status becomes "ready".
     exec_start apexd-snapshotde
 
-    # Set SELinux security contexts on upgrade or policy update.
-    restorecon --recursive --skip-ce /data
-
     # Check any timezone data in /data is newer than the copy in the time zone data
     # module, delete if not.
     exec - system system -- /system/bin/tzdatacheck /apex/com.android.tzdata/etc/tz /data/misc/zoneinfo
@@ -884,6 +939,9 @@
     write /proc/sys/vm/dirty_expire_centisecs 200
     write /proc/sys/vm/dirty_background_ratio  5
 
+on property:sys.boot_completed=1 && property:init.mount_debugfs=1
+   umount /sys/kernel/debug
+
 on boot
     # basic network init
     ifup lo
@@ -990,9 +1048,6 @@
 
     class_start core
 
-    # Requires keystore (currently a core service) to be ready first.
-    exec -- /system/bin/fsverity_init
-
 on nonencrypted
     class_start main
     class_start late_start
@@ -1025,6 +1080,7 @@
     class_start main
     class_start late_start
     setprop service.bootanim.exit 0
+    setprop service.bootanim.progress 0
     start bootanim
 
 on property:vold.decrypt=trigger_shutdown_framework
@@ -1126,6 +1182,7 @@
   setprop sys.user.0.ce_available ""
   setprop sys.shutdown.requested ""
   setprop service.bootanim.exit ""
+  setprop service.bootanim.progress ""
 
 on userspace-reboot-fs-remount
   # Make sure that vold is running.
@@ -1152,3 +1209,11 @@
 
 on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
   setprop sys.init.userspace_reboot.in_progress ""
+
+on early-init && property:init.mount_debugfs=1
+    mount debugfs debugfs /sys/kernel/debug
+    chmod 0755 /sys/kernel/debug
+
+# Migrate tasks again in case kernel threads are created during boot
+on property:sys.boot_completed=1
+  copy_per_line /dev/cpuctl/tasks /dev/cpuctl/system/tasks
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 1994bdb..aebcd5f 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,3 +1,7 @@
+import /vendor/ueventd.rc
+import /odm/ueventd.rc
+import /ueventd.${ro.hardware}.rc
+
 firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/
 uevent_socket_rcvbuf_size 16M
 
@@ -42,7 +46,9 @@
 /dev/vndbinder            0666   root       root
 
 /dev/pmsg0                0222   root       log
-/dev/dma_heap/system      0666   system     system
+/dev/dma_heap/system      0444   system     system
+/dev/dma_heap/system-uncached      0444   system     system
+/dev/dma_heap/system-secure        0444   system     system
 
 # kms driver for drm based gpu
 /dev/dri/*                0666   root       graphics
@@ -71,3 +77,5 @@
 /sys/devices/virtual/usb_composite/*   enable      0664  root   system
 /sys/devices/system/cpu/cpu*   cpufreq/scaling_max_freq   0664  system system
 /sys/devices/system/cpu/cpu*   cpufreq/scaling_min_freq   0664  system system
+/sys/devices/virtual/misc/uhid/*/leds/* brightness   0664  system system
+/sys/devices/virtual/misc/uhid/*/leds/* multi_intensity   0664  system system
diff --git a/trusty/confirmationui/fuzz/Android.bp b/trusty/confirmationui/fuzz/Android.bp
new file mode 100644
index 0000000..635966f
--- /dev/null
+++ b/trusty/confirmationui/fuzz/Android.bp
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_fuzz {
+    name: "trusty_confirmationui_fuzzer",
+    defaults: ["trusty_fuzzer_defaults"],
+    srcs: ["fuzz.cpp"],
+
+    // The initial corpus for this fuzzer was derived by dumping bytes from
+    // ConfirmationUI VTS.
+    corpus: ["corpus/*"],
+}
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-2ekYc2 b/trusty/confirmationui/fuzz/corpus/confirmationui-2ekYc2
new file mode 100644
index 0000000..53fe0c9
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-2ekYc2
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-5yTG3f b/trusty/confirmationui/fuzz/corpus/confirmationui-5yTG3f
new file mode 100644
index 0000000..d627b01
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-5yTG3f
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-6l8Soq b/trusty/confirmationui/fuzz/corpus/confirmationui-6l8Soq
new file mode 100644
index 0000000..bda80fd
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-6l8Soq
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-7kFpGO b/trusty/confirmationui/fuzz/corpus/confirmationui-7kFpGO
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-7kFpGO
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-92m2f3 b/trusty/confirmationui/fuzz/corpus/confirmationui-92m2f3
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-92m2f3
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-ALYIzO b/trusty/confirmationui/fuzz/corpus/confirmationui-ALYIzO
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-ALYIzO
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-AcIMhR b/trusty/confirmationui/fuzz/corpus/confirmationui-AcIMhR
new file mode 100644
index 0000000..f5854f8
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-AcIMhR
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-AieaIi b/trusty/confirmationui/fuzz/corpus/confirmationui-AieaIi
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-AieaIi
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-BdqX5j b/trusty/confirmationui/fuzz/corpus/confirmationui-BdqX5j
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-BdqX5j
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-JBPIGs b/trusty/confirmationui/fuzz/corpus/confirmationui-JBPIGs
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-JBPIGs
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-MWHw4T b/trusty/confirmationui/fuzz/corpus/confirmationui-MWHw4T
new file mode 100644
index 0000000..0dc6e91
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-MWHw4T
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-TZzVLO b/trusty/confirmationui/fuzz/corpus/confirmationui-TZzVLO
new file mode 100644
index 0000000..927d64d
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-TZzVLO
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-WwdA3B b/trusty/confirmationui/fuzz/corpus/confirmationui-WwdA3B
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-WwdA3B
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-globJV b/trusty/confirmationui/fuzz/corpus/confirmationui-globJV
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-globJV
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-hzUgjD b/trusty/confirmationui/fuzz/corpus/confirmationui-hzUgjD
new file mode 100644
index 0000000..87870ca
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-hzUgjD
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-jXC78o b/trusty/confirmationui/fuzz/corpus/confirmationui-jXC78o
new file mode 100644
index 0000000..0b274bf
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-jXC78o
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-kykxni b/trusty/confirmationui/fuzz/corpus/confirmationui-kykxni
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-kykxni
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-npHe8t b/trusty/confirmationui/fuzz/corpus/confirmationui-npHe8t
new file mode 100644
index 0000000..87870ca
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-npHe8t
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-rPgnyI b/trusty/confirmationui/fuzz/corpus/confirmationui-rPgnyI
new file mode 100644
index 0000000..87870ca
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-rPgnyI
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-uCJ1Me b/trusty/confirmationui/fuzz/corpus/confirmationui-uCJ1Me
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-uCJ1Me
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-wAQEjK b/trusty/confirmationui/fuzz/corpus/confirmationui-wAQEjK
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-wAQEjK
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-xjtOks b/trusty/confirmationui/fuzz/corpus/confirmationui-xjtOks
new file mode 100644
index 0000000..b4a1c49
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-xjtOks
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-zKFIjN b/trusty/confirmationui/fuzz/corpus/confirmationui-zKFIjN
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-zKFIjN
Binary files differ
diff --git a/trusty/confirmationui/fuzz/fuzz.cpp b/trusty/confirmationui/fuzz/fuzz.cpp
new file mode 100644
index 0000000..df2517c
--- /dev/null
+++ b/trusty/confirmationui/fuzz/fuzz.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 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 <iostream>
+#include <stdlib.h>
+#include <trusty/coverage/coverage.h>
+#include <trusty/fuzz/counters.h>
+#include <trusty/fuzz/utils.h>
+#include <unistd.h>
+
+using android::trusty::coverage::CoverageRecord;
+using android::trusty::fuzz::ExtraCounters;
+using android::trusty::fuzz::TrustyApp;
+
+#define TIPC_DEV "/dev/trusty-ipc-dev0"
+#define CONFIRMATIONUI_PORT "com.android.trusty.confirmationui"
+#define CONFIRMATIONUI_MODULE_NAME "confirmationui.syms.elf"
+
+/* ConfirmationUI TA's UUID is 7dee2364-c036-425b-b086-df0f6c233c1b */
+static struct uuid confirmationui_uuid = {
+    0x7dee2364,
+    0xc036,
+    0x425b,
+    {0xb0, 0x86, 0xdf, 0x0f, 0x6c, 0x23, 0x3c, 0x1b},
+};
+
+/* The format of the packets is as following:
+ * 16 bits (uint16_t, header) + payload bytes
+ * The 16 bits header spicify the number of bytes of payload (header excluded).
+ */
+struct data_packet {
+    uint16_t header;
+    uint8_t payload[];
+};
+
+static CoverageRecord record(TIPC_DEV, &confirmationui_uuid, CONFIRMATIONUI_MODULE_NAME);
+
+extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
+    auto ret = record.Open();
+    if (!ret.ok()) {
+        std::cerr << ret.error() << std::endl;
+        exit(-1);
+    }
+    return 0;
+}
+
+/* Each corpus contains one or more data packets. */
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    static uint8_t buf[TIPC_MAX_MSG_SIZE];
+    size_t data_idx = 0;
+
+    ExtraCounters counters(&record);
+    counters.Reset();
+
+    TrustyApp ta(TIPC_DEV, CONFIRMATIONUI_PORT);
+    auto ret = ta.Connect();
+    if (!ret.ok()) {
+        android::trusty::fuzz::Abort();
+    }
+
+    while (data_idx < size) {
+        struct data_packet* data_packet_ptr = (struct data_packet*)&data[data_idx];
+        size_t payload_size = data_packet_ptr->header;
+        data_idx += data_packet_ptr->header + sizeof(data_packet_ptr->header);
+
+        /* Write message to confirmationui server */
+        ret = ta.Write(data_packet_ptr->payload, payload_size);
+        if (!ret.ok()) {
+            return -1;
+        }
+
+        /* Read message from confirmationui server */
+        ret = ta.Read(&buf, sizeof(buf));
+        if (!ret.ok()) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
diff --git a/trusty/coverage/Android.bp b/trusty/coverage/Android.bp
new file mode 100644
index 0000000..daa6f03
--- /dev/null
+++ b/trusty/coverage/Android.bp
@@ -0,0 +1,50 @@
+// Copyright (C) 2020 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library {
+    name: "libtrusty_coverage",
+    vendor_available: true,
+    srcs: [
+        "coverage.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    static_libs: [
+        "libtrusty",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libdmabufheap",
+    ],
+}
+
+cc_test {
+    name: "libtrusty_coverage_test",
+    srcs: [
+        "coverage_test.cpp",
+    ],
+    static_libs: [
+        "libtrusty_coverage",
+        "libtrusty",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libdmabufheap",
+    ],
+    require_root: true,
+}
diff --git a/trusty/coverage/coverage.cpp b/trusty/coverage/coverage.cpp
new file mode 100644
index 0000000..5eccdc5
--- /dev/null
+++ b/trusty/coverage/coverage.cpp
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2020 The Android Open Sourete Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "coverage"
+
+#include <BufferAllocator/BufferAllocator.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <assert.h>
+#include <log/log.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+#include <trusty/coverage/coverage.h>
+#include <trusty/coverage/record.h>
+#include <trusty/coverage/tipc.h>
+#include <trusty/tipc.h>
+
+#define COVERAGE_CLIENT_PORT "com.android.trusty.coverage.client"
+
+namespace android {
+namespace trusty {
+namespace coverage {
+
+using android::base::ErrnoError;
+using android::base::Error;
+using std::string;
+using std::to_string;
+using std::unique_ptr;
+
+static inline uintptr_t RoundPageUp(uintptr_t val) {
+    return (val + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
+}
+
+CoverageRecord::CoverageRecord(string tipc_dev, struct uuid* uuid)
+    : tipc_dev_(std::move(tipc_dev)),
+      coverage_srv_fd_(-1),
+      uuid_(*uuid),
+      sancov_filename_(),
+      record_len_(0),
+      shm_(NULL),
+      shm_len_(0) {}
+
+CoverageRecord::CoverageRecord(string tipc_dev, struct uuid* uuid, string module_name)
+    : tipc_dev_(std::move(tipc_dev)),
+      coverage_srv_fd_(-1),
+      uuid_(*uuid),
+      sancov_filename_(module_name + "." + to_string(getpid()) + ".sancov"),
+      record_len_(0),
+      shm_(NULL),
+      shm_len_(0) {}
+
+CoverageRecord::~CoverageRecord() {
+    if (shm_) {
+        if (sancov_filename_) {
+            auto res = SaveSancovFile(*sancov_filename_);
+            if (!res.ok()) {
+                ALOGE("Could not write sancov file for module: %s\n", sancov_filename_->c_str());
+            }
+        }
+
+        munmap((void*)shm_, shm_len_);
+    }
+}
+
+Result<void> CoverageRecord::Rpc(coverage_client_req* req, int req_fd, coverage_client_resp* resp) {
+    int rc;
+
+    if (req_fd < 0) {
+        rc = write(coverage_srv_fd_, req, sizeof(*req));
+    } else {
+        iovec iov = {
+                .iov_base = req,
+                .iov_len = sizeof(*req),
+        };
+
+        trusty_shm shm = {
+                .fd = req_fd,
+                .transfer = TRUSTY_SHARE,
+        };
+
+        rc = tipc_send(coverage_srv_fd_, &iov, 1, &shm, 1);
+    }
+
+    if (rc != (int)sizeof(*req)) {
+        return ErrnoError() << "failed to send request to coverage server: ";
+    }
+
+    rc = read(coverage_srv_fd_, resp, sizeof(*resp));
+    if (rc != (int)sizeof(*resp)) {
+        return ErrnoError() << "failed to read reply from coverage server: ";
+    }
+
+    if (resp->hdr.cmd != (req->hdr.cmd | COVERAGE_CLIENT_CMD_RESP_BIT)) {
+        return ErrnoError() << "unknown response cmd: " << resp->hdr.cmd;
+    }
+
+    return {};
+}
+
+Result<void> CoverageRecord::Open() {
+    coverage_client_req req;
+    coverage_client_resp resp;
+
+    if (shm_) {
+        return {}; /* already initialized */
+    }
+
+    int fd = tipc_connect(tipc_dev_.c_str(), COVERAGE_CLIENT_PORT);
+    if (fd < 0) {
+        return ErrnoError() << "failed to connect to Trusty coverarge server: ";
+    }
+    coverage_srv_fd_.reset(fd);
+
+    req.hdr.cmd = COVERAGE_CLIENT_CMD_OPEN;
+    req.open_args.uuid = uuid_;
+    auto ret = Rpc(&req, -1, &resp);
+    if (!ret.ok()) {
+        return Error() << "failed to open coverage client: ";
+    }
+    record_len_ = resp.open_args.record_len;
+    shm_len_ = RoundPageUp(record_len_);
+
+    BufferAllocator allocator;
+
+    fd = allocator.Alloc("system", shm_len_);
+    if (fd < 0) {
+        return ErrnoError() << "failed to create dmabuf of size " << shm_len_
+                            << " err code: " << fd;
+    }
+    unique_fd dma_buf(fd);
+
+    void* shm = mmap(0, shm_len_, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
+    if (shm == MAP_FAILED) {
+        return ErrnoError() << "failed to map memfd: ";
+    }
+
+    req.hdr.cmd = COVERAGE_CLIENT_CMD_SHARE_RECORD;
+    req.share_record_args.shm_len = shm_len_;
+    ret = Rpc(&req, dma_buf, &resp);
+    if (!ret.ok()) {
+        return Error() << "failed to send shared memory: ";
+    }
+
+    shm_ = shm;
+    return {};
+}
+
+void CoverageRecord::ResetFullRecord() {
+    auto header_region = GetRegionBounds(COV_START);
+    if (!header_region.ok()) {
+        // If the header cannot be parsed, we can't reset the proper region yet.
+        return;
+    }
+
+    for (size_t i = header_region->second; i < shm_len_; i++) {
+        *((volatile uint8_t*)shm_ + i) = 0;
+    }
+}
+
+void CoverageRecord::ResetCounts() {
+    volatile uint8_t* begin = nullptr;
+    volatile uint8_t* end = nullptr;
+    GetRawCounts(&begin, &end);
+
+    for (volatile uint8_t* x = begin; x < end; x++) {
+        *x = 0;
+    }
+}
+
+void CoverageRecord::ResetPCs() {
+    volatile uintptr_t* begin = nullptr;
+    volatile uintptr_t* end = nullptr;
+    GetRawPCs(&begin, &end);
+
+    for (volatile uintptr_t* x = begin; x < end; x++) {
+        *x = 0;
+    }
+}
+
+Result<std::pair<size_t, size_t>> CoverageRecord::GetRegionBounds(uint32_t region_type) {
+    assert(shm_);
+
+    auto header = (volatile struct coverage_record_header*)shm_;
+
+    if (header->type != COV_START) {
+        return Error() << "Header not yet valid";
+    }
+
+    for (++header; header->type != COV_TOTAL_LENGTH; ++header) {
+        if (header->type == region_type) {
+            // Coverage record must end with a COV_TOTAL_LENGTH header entry, so
+            // it is always safe to read the next entry since we don't iterate
+            // over the COV_TOTAL_LENGTH entry.
+            return {{header->offset, (header + 1)->offset}};
+        }
+    }
+
+    return Error() << "Could not find coverage region type: " << region_type;
+}
+
+void CoverageRecord::GetRawData(volatile void** begin, volatile void** end) {
+    assert(shm_);
+
+    *begin = shm_;
+    *end = (uint8_t*)(*begin) + record_len_;
+}
+
+void CoverageRecord::GetRawCounts(volatile uint8_t** begin, volatile uint8_t** end) {
+    auto region = GetRegionBounds(COV_8BIT_COUNTERS);
+    if (!region.ok()) {
+        *begin = 0;
+        *end = 0;
+        return;
+    }
+
+    assert(region->second <= record_len_);
+
+    *begin = (volatile uint8_t*)shm_ + region->first;
+    *end = (volatile uint8_t*)shm_ + region->second;
+}
+
+void CoverageRecord::GetRawPCs(volatile uintptr_t** begin, volatile uintptr_t** end) {
+    auto region = GetRegionBounds(COV_INSTR_PCS);
+    if (!region.ok()) {
+        *begin = 0;
+        *end = 0;
+        return;
+    }
+
+    assert(region->second <= record_len_);
+
+    *begin = (volatile uintptr_t*)((volatile uint8_t*)shm_ + region->first);
+    *end = (volatile uintptr_t*)((volatile uint8_t*)shm_ + region->second);
+}
+
+uint64_t CoverageRecord::TotalEdgeCounts() {
+    assert(shm_);
+
+    uint64_t counter = 0;
+
+    volatile uint8_t* begin = NULL;
+    volatile uint8_t* end = NULL;
+
+    GetRawCounts(&begin, &end);
+
+    for (volatile uint8_t* x = begin; x < end; x++) {
+        counter += *x;
+    }
+
+    return counter;
+}
+
+Result<void> CoverageRecord::SaveSancovFile(const std::string& filename) {
+    android::base::unique_fd output_fd(TEMP_FAILURE_RETRY(creat(filename.c_str(), 00644)));
+    if (!output_fd.ok()) {
+        return ErrnoError() << "Could not open sancov file";
+    }
+
+    uint64_t magic;
+    if (sizeof(uintptr_t) == 8) {
+        magic = 0xC0BFFFFFFFFFFF64;
+    } else if (sizeof(uintptr_t) == 4) {
+        magic = 0xC0BFFFFFFFFFFF32;
+    }
+    WriteFully(output_fd, &magic, sizeof(magic));
+
+    volatile uintptr_t* begin = nullptr;
+    volatile uintptr_t* end = nullptr;
+
+    GetRawPCs(&begin, &end);
+
+    for (volatile uintptr_t* pc_ptr = begin; pc_ptr < end; pc_ptr++) {
+        uintptr_t pc = *pc_ptr;
+        if (pc) {
+            WriteFully(output_fd, &pc, sizeof(pc));
+        }
+    }
+
+    return {};
+}
+
+}  // namespace coverage
+}  // namespace trusty
+}  // namespace android
diff --git a/trusty/coverage/coverage_test.cpp b/trusty/coverage/coverage_test.cpp
new file mode 100644
index 0000000..c1efca6
--- /dev/null
+++ b/trusty/coverage/coverage_test.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+#include <trusty/coverage/coverage.h>
+#include <trusty/tipc.h>
+#include <array>
+#include <memory>
+
+using android::base::unique_fd;
+using std::array;
+using std::make_unique;
+using std::unique_ptr;
+
+#define TIPC_DEV "/dev/trusty-ipc-dev0"
+#define TEST_SRV_PORT "com.android.trusty.sancov.test.srv"
+#define TEST_SRV_MODULE "srv.syms.elf"
+
+namespace android {
+namespace trusty {
+namespace coverage {
+
+/* Test server's UUID is 77f68803-c514-43ba-bdce-3254531c3d24 */
+static struct uuid test_srv_uuid = {
+        0x77f68803,
+        0xc514,
+        0x43ba,
+        {0xbd, 0xce, 0x32, 0x54, 0x53, 0x1c, 0x3d, 0x24},
+};
+
+class CoverageTest : public ::testing::Test {
+  public:
+    void SetUp() override {
+        record_ = make_unique<CoverageRecord>(TIPC_DEV, &test_srv_uuid);
+        auto ret = record_->Open();
+        ASSERT_TRUE(ret.ok()) << ret.error();
+    }
+
+    void TearDown() override { record_.reset(); }
+
+    unique_ptr<CoverageRecord> record_;
+};
+
+TEST_F(CoverageTest, CoverageReset) {
+    record_->ResetFullRecord();
+    auto counter = record_->TotalEdgeCounts();
+    ASSERT_EQ(counter, 0);
+}
+
+TEST_F(CoverageTest, TestServerCoverage) {
+    unique_fd test_srv(tipc_connect(TIPC_DEV, TEST_SRV_PORT));
+    ASSERT_GE(test_srv, 0);
+
+    uint32_t mask = (uint32_t)-1;
+    uint32_t magic = 0xdeadbeef;
+    uint64_t high_watermark = 0;
+
+    for (size_t i = 1; i < sizeof(magic) * 8; i++) {
+        /* Reset coverage */
+        record_->ResetCounts();
+
+        /* Send message to test server */
+        uint32_t msg = magic & ~(mask << i);
+        int rc = write(test_srv, &msg, sizeof(msg));
+        ASSERT_EQ(rc, sizeof(msg));
+
+        /* Read message from test server */
+        rc = read(test_srv, &msg, sizeof(msg));
+        ASSERT_EQ(rc, sizeof(msg));
+
+        /* Count number of non-unique blocks executed */
+        auto counter = record_->TotalEdgeCounts();
+        /* Each consecutive input should exercise more or same blocks */
+        ASSERT_GE(counter, high_watermark);
+        high_watermark = counter;
+
+        auto sancov_filename = android::base::StringPrintf(
+                "/data/local/tmp/" TEST_SRV_MODULE ".%d.sancov", getpid());
+        auto res = record_->SaveSancovFile(sancov_filename);
+        ASSERT_TRUE(res.ok());
+    }
+
+    ASSERT_GT(high_watermark, 0);
+}
+
+}  // namespace coverage
+}  // namespace trusty
+}  // namespace android
diff --git a/trusty/coverage/include/trusty/coverage/coverage.h b/trusty/coverage/include/trusty/coverage/coverage.h
new file mode 100644
index 0000000..5da68da
--- /dev/null
+++ b/trusty/coverage/include/trusty/coverage/coverage.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+#include <string>
+
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+#include <stdint.h>
+#include <trusty/coverage/tipc.h>
+
+namespace android {
+namespace trusty {
+namespace coverage {
+
+using android::base::Result;
+using android::base::unique_fd;
+
+class CoverageRecord {
+  public:
+    /**
+     * Create a coverage record interface. Coverage will not be written to a
+     * sancov output file on completion.
+     */
+    CoverageRecord(std::string tipc_dev, struct uuid* uuid);
+
+    /**
+     * Create a coverage record interface. On destruction, write this coverage
+     * to the given sancov filename.
+     */
+    CoverageRecord(std::string tipc_dev, struct uuid* uuid, std::string module_name);
+
+    ~CoverageRecord();
+    Result<void> Open();
+    void ResetFullRecord();
+    void ResetCounts();
+    void ResetPCs();
+    void GetRawData(volatile void** begin, volatile void** end);
+    void GetRawCounts(volatile uint8_t** begin, volatile uint8_t** end);
+    void GetRawPCs(volatile uintptr_t** begin, volatile uintptr_t** end);
+    uint64_t TotalEdgeCounts();
+
+    /**
+     * Save the current set of observed PCs to the given filename.
+     * The resulting .sancov file can be parsed via the LLVM sancov tool to see
+     * coverage statistics and visualize coverage.
+     */
+    Result<void> SaveSancovFile(const std::string& filename);
+
+  private:
+    Result<void> Rpc(coverage_client_req* req, int req_fd, coverage_client_resp* resp);
+
+    Result<std::pair<size_t, size_t>> GetRegionBounds(uint32_t region_type);
+
+    std::string tipc_dev_;
+    unique_fd coverage_srv_fd_;
+    struct uuid uuid_;
+    std::optional<std::string> sancov_filename_;
+    size_t record_len_;
+    volatile void* shm_;
+    size_t shm_len_;
+};
+
+}  // namespace coverage
+}  // namespace trusty
+}  // namespace android
diff --git a/trusty/coverage/include/trusty/coverage/record.h b/trusty/coverage/include/trusty/coverage/record.h
new file mode 100644
index 0000000..bfe06f3
--- /dev/null
+++ b/trusty/coverage/include/trusty/coverage/record.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file needs to be kept in-sync with its counterpart on Trusty side:
+ * trusty/user/base/lib/coverage/common/include/lib/coverage/common/record.h */
+
+#pragma once
+
+#include <stdint.h>
+
+/**
+ * enum coverage_record_type - Coverage region header type
+ * @COV_START: Magic header start marker
+ * @COV_8BIT_COUNTERS: 8bit counter for each instrumentation point
+ * @COV_INSTR_PCS: Pointer length offset of each instrumentation point from the
+ *                 start of the binary
+ * @COV_TOTAL_LENGTH: Total length of the entire coverage record, must be the
+ *                    last header item.
+ *
+ * Describes the type of a region of the coverage record. See &struct
+ * coverage_record_header.
+ */
+enum coverage_record_type {
+    COV_START = 0x434f5652,
+    COV_8BIT_COUNTERS = 1,
+    COV_INSTR_PCS = 2,
+    COV_TOTAL_LENGTH = 0,
+};
+
+/**
+ * struct coverage_record_header - Header entry describing a region of the
+ * coverage record.
+ * @type: type of the region, must be one of @enum coverage_record_type
+ * @offset: offset from the beginning of the header to the start of the region
+ *
+ * Coverage records start with a header which is a list of struct
+ * coverage_record_header, beginning with an entry with type COV_START and
+ * terminated with an entry with type COV_TOTAL_LENGTH. Each of these header
+ * entries corresponds to a region of the record, with the offset indicating the
+ * offset of the start of that region from the beginning of the record (i.e. the
+ * beginning of the header). Each record type and offset is 32-bit field with
+ * native endianness. The first header item must be COV_START with a 0 offset.
+ * The COV_START entry should be initialized when the coverage header is
+ * complete and ready for consumption by the client, because coverage record
+ * initialization happens asynchronously. The final header item,
+ * COV_TOTAL_LENGTH, which must always be present, indicates the total length of
+ * the coverage record, including the header.
+ *
+ * Coverage regions should be contiguous, so the end of one region is the start
+ * of the next, and the coverage header must be in the same order as the regions
+ * in the record body. Thus we can compute the length of a region by subtracting
+ * the region's offset from the offset of the next header item.
+ */
+struct coverage_record_header {
+    uint32_t type;
+    uint32_t offset;
+};
diff --git a/trusty/coverage/include/trusty/coverage/tipc.h b/trusty/coverage/include/trusty/coverage/tipc.h
new file mode 100644
index 0000000..c4157c4
--- /dev/null
+++ b/trusty/coverage/include/trusty/coverage/tipc.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file needs to be kept in-sync with it's counterpart on Trusty side */
+
+#pragma once
+
+#include <stdint.h>
+
+#define COVERAGE_CLIENT_PORT "com.android.trusty.coverage.client"
+
+struct uuid {
+    uint32_t time_low;
+    uint16_t time_mid;
+    uint16_t time_hi_and_version;
+    uint8_t clock_seq_and_node[8];
+};
+
+enum coverage_client_cmd {
+    COVERAGE_CLIENT_CMD_RESP_BIT = 1U,
+    COVERAGE_CLIENT_CMD_SHIFT = 1U,
+    COVERAGE_CLIENT_CMD_OPEN = (1U << COVERAGE_CLIENT_CMD_SHIFT),
+    COVERAGE_CLIENT_CMD_SHARE_RECORD = (2U << COVERAGE_CLIENT_CMD_SHIFT),
+};
+
+struct coverage_client_hdr {
+    uint32_t cmd;
+};
+
+struct coverage_client_open_req {
+    struct uuid uuid;
+};
+
+struct coverage_client_open_resp {
+    uint32_t record_len;
+};
+
+struct coverage_client_share_record_req {
+    uint32_t shm_len;
+};
+
+struct coverage_client_req {
+    struct coverage_client_hdr hdr;
+    union {
+        struct coverage_client_open_req open_args;
+        struct coverage_client_share_record_req share_record_args;
+    };
+};
+
+struct coverage_client_resp {
+    struct coverage_client_hdr hdr;
+    union {
+        struct coverage_client_open_resp open_args;
+    };
+};
diff --git a/trusty/fuzz/Android.bp b/trusty/fuzz/Android.bp
index 969431c..ad13816 100644
--- a/trusty/fuzz/Android.bp
+++ b/trusty/fuzz/Android.bp
@@ -14,10 +14,9 @@
 
 cc_defaults {
     name: "trusty_fuzzer_defaults",
-    static_libs: [
-        "libtrusty_fuzz_utils",
-    ],
     shared_libs: [
+        "libtrusty_coverage",
+        "libtrusty_fuzz_utils",
         "libbase",
         "liblog",
     ],
@@ -33,9 +32,17 @@
 
 cc_library {
     name: "libtrusty_fuzz_utils",
-    srcs: ["utils.cpp"],
+    srcs: [
+        "counters.cpp",
+        "utils.cpp",
+    ],
     export_include_dirs: ["include"],
+    static_libs: [
+        "libFuzzer",
+        "libtrusty",
+    ],
     shared_libs: [
+        "libtrusty_coverage",
         "libbase",
         "liblog",
     ],
diff --git a/trusty/fuzz/counters.cpp b/trusty/fuzz/counters.cpp
new file mode 100644
index 0000000..1e863ac
--- /dev/null
+++ b/trusty/fuzz/counters.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 The Android Open Sourete Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "trusty-fuzz-counters"
+
+#include <FuzzerDefs.h>
+
+#include <trusty/fuzz/counters.h>
+
+#include <android-base/logging.h>
+#include <log/log.h>
+#include <trusty/coverage/coverage.h>
+#include <trusty/coverage/tipc.h>
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+
+/*
+ * We don't know how many counters the coverage record will contain. So, eyeball
+ * the size of this section.
+ */
+static const size_t kMaxNumCounters = 0x4000;
+__attribute__((section("__libfuzzer_extra_counters"))) volatile uint8_t counters[kMaxNumCounters];
+
+namespace android {
+namespace trusty {
+namespace fuzz {
+
+ExtraCounters::ExtraCounters(coverage::CoverageRecord* record) : record_(record) {
+    assert(fuzzer::ExtraCountersBegin());
+    assert(fuzzer::ExtraCountersEnd());
+
+    volatile uint8_t* begin = NULL;
+    volatile uint8_t* end = NULL;
+    record_->GetRawCounts(&begin, &end);
+    assert(end - begin <= sizeof(counters));
+}
+
+ExtraCounters::~ExtraCounters() {
+    Flush();
+}
+
+void ExtraCounters::Reset() {
+    record_->ResetCounts();
+    fuzzer::ClearExtraCounters();
+}
+
+void ExtraCounters::Flush() {
+    volatile uint8_t* begin = NULL;
+    volatile uint8_t* end = NULL;
+
+    record_->GetRawCounts(&begin, &end);
+    if (!begin || !end) {
+        ALOGE("Could not get raw counts from coverage record\n");
+        return;
+    }
+
+    size_t num_counters = end - begin;
+    if (num_counters > kMaxNumCounters) {
+        ALOGE("Too many counters (%zu) to fit in the extra counters section!\n", num_counters);
+        num_counters = kMaxNumCounters;
+    }
+    for (size_t i = 0; i < num_counters; i++) {
+        *(counters + i) = *(begin + i);
+    }
+}
+
+}  // namespace fuzz
+}  // namespace trusty
+}  // namespace android
diff --git a/libstats/socket/include/stats_socket.h b/trusty/fuzz/include/trusty/fuzz/counters.h
similarity index 60%
rename from libstats/socket/include/stats_socket.h
rename to trusty/fuzz/include/trusty/fuzz/counters.h
index 5a75fc0..db933d9 100644
--- a/libstats/socket/include/stats_socket.h
+++ b/trusty/fuzz/include/trusty/fuzz/counters.h
@@ -16,18 +16,27 @@
 
 #pragma once
 
-/**
- * Helpers to manage the statsd socket.
- **/
+#include <string>
 
-#ifdef __cplusplus
-extern "C" {
-#endif  // __CPLUSPLUS
+#include <android-base/result.h>
+#include <trusty/coverage/coverage.h>
 
-/**
- * Closes the statsd socket file descriptor.
- **/
-void AStatsSocket_close();
-#ifdef __cplusplus
-}
-#endif  // __CPLUSPLUS
+namespace android {
+namespace trusty {
+namespace fuzz {
+
+class ExtraCounters {
+  public:
+    ExtraCounters(coverage::CoverageRecord* record);
+    ~ExtraCounters();
+
+    void Reset();
+    void Flush();
+
+  private:
+    coverage::CoverageRecord* record_;
+};
+
+}  // namespace fuzz
+}  // namespace trusty
+}  // namespace android
diff --git a/deprecated-adf/libadf/tests/Android.bp b/trusty/fuzz/test/Android.bp
similarity index 66%
copy from deprecated-adf/libadf/tests/Android.bp
copy to trusty/fuzz/test/Android.bp
index 9b3430e..66e103d 100644
--- a/deprecated-adf/libadf/tests/Android.bp
+++ b/trusty/fuzz/test/Android.bp
@@ -1,23 +1,19 @@
-//
-// Copyright (C) 2013 The Android Open Source Project
+// Copyright (C) 2020 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
+//       http://www.apache.org/licenses/LICENSE-2.0
 //
 // Unless required by applicable law or agreed to in writing, software
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-//
 
-cc_test {
-    name: "adf-unit-tests",
-    srcs: ["adf_test.cpp"],
-    shared_libs: ["libsync"],
-    static_libs: ["libadf"],
-    cflags: ["-Werror"],
+cc_fuzz {
+    name: "trusty_test_fuzzer",
+    defaults: ["trusty_fuzzer_defaults"],
+    srcs: ["fuzz.cpp"],
 }
diff --git a/trusty/fuzz/test/fuzz.cpp b/trusty/fuzz/test/fuzz.cpp
new file mode 100644
index 0000000..e7913db
--- /dev/null
+++ b/trusty/fuzz/test/fuzz.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 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 <stdlib.h>
+#include <trusty/coverage/coverage.h>
+#include <trusty/fuzz/counters.h>
+#include <trusty/fuzz/utils.h>
+#include <unistd.h>
+#include <iostream>
+
+using android::trusty::coverage::CoverageRecord;
+using android::trusty::fuzz::ExtraCounters;
+using android::trusty::fuzz::TrustyApp;
+
+#define TIPC_DEV "/dev/trusty-ipc-dev0"
+#define TEST_SRV_PORT "com.android.trusty.sancov.test.srv"
+
+/* Test server's UUID is 77f68803-c514-43ba-bdce-3254531c3d24 */
+static struct uuid test_srv_uuid = {
+        0x77f68803,
+        0xc514,
+        0x43ba,
+        {0xbd, 0xce, 0x32, 0x54, 0x53, 0x1c, 0x3d, 0x24},
+};
+
+static CoverageRecord record(TIPC_DEV, &test_srv_uuid);
+
+extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
+    auto ret = record.Open();
+    if (!ret.ok()) {
+        std::cerr << ret.error() << std::endl;
+        exit(-1);
+    }
+    return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    static uint8_t buf[TIPC_MAX_MSG_SIZE];
+
+    ExtraCounters counters(&record);
+    counters.Reset();
+
+    TrustyApp ta(TIPC_DEV, TEST_SRV_PORT);
+    auto ret = ta.Connect();
+    if (!ret.ok()) {
+        android::trusty::fuzz::Abort();
+    }
+
+    /* Send message to test server */
+    ret = ta.Write(data, size);
+    if (!ret.ok()) {
+        return -1;
+    }
+
+    /* Read message from test server */
+    ret = ta.Read(&buf, sizeof(buf));
+    if (!ret.ok()) {
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/trusty/fuzz/utils.cpp b/trusty/fuzz/utils.cpp
index 240afe7..f4cf0b6 100644
--- a/trusty/fuzz/utils.cpp
+++ b/trusty/fuzz/utils.cpp
@@ -25,6 +25,7 @@
 #include <linux/uio.h>
 #include <log/log_read.h>
 #include <time.h>
+#include <trusty/tipc.h>
 #include <iostream>
 
 using android::base::ErrnoError;
@@ -32,9 +33,6 @@
 using android::base::Result;
 using android::base::unique_fd;
 
-#define TIPC_IOC_MAGIC 'r'
-#define TIPC_IOC_CONNECT _IOW(TIPC_IOC_MAGIC, 0x80, char*)
-
 namespace {
 
 const size_t kTimeoutSeconds = 5;
@@ -80,27 +78,14 @@
     : tipc_dev_(tipc_dev), ta_port_(ta_port), ta_fd_(-1) {}
 
 Result<void> TrustyApp::Connect() {
-    /*
-     * TODO: We can't use libtrusty because (yet)
-     * (1) cc_fuzz can't deal with vendor components (b/170753563)
-     * (2) We need non-blocking behavior to detect Trusty going down.
-     * (we could implement the timeout in the fuzzing code though, as
-     * it needs to be around the call to read())
-     */
     alarm(kTimeoutSeconds);
-    int fd = open(tipc_dev_.c_str(), O_RDWR);
+    int fd = tipc_connect(tipc_dev_.c_str(), ta_port_.c_str());
     alarm(0);
     if (fd < 0) {
         return ErrnoError() << "failed to open TIPC device: ";
     }
     ta_fd_.reset(fd);
 
-    // This ioctl will time out in the kernel if it can't connect.
-    int rc = TEMP_FAILURE_RETRY(ioctl(ta_fd_, TIPC_IOC_CONNECT, ta_port_.c_str()));
-    if (rc < 0) {
-        return ErrnoError() << "failed to connect to TIPC service: ";
-    }
-
     return {};
 }
 
diff --git a/trusty/gatekeeper/fuzz/fuzz.cpp b/trusty/gatekeeper/fuzz/fuzz.cpp
index f8ec931..7bfd7d1 100644
--- a/trusty/gatekeeper/fuzz/fuzz.cpp
+++ b/trusty/gatekeeper/fuzz/fuzz.cpp
@@ -14,27 +14,48 @@
  * limitations under the License.
  */
 
-#undef NDEBUG
-
-#include <assert.h>
-#include <log/log.h>
 #include <stdlib.h>
+#include <trusty/coverage/coverage.h>
+#include <trusty/fuzz/counters.h>
 #include <trusty/fuzz/utils.h>
 #include <unistd.h>
+#include <iostream>
+
+using android::trusty::coverage::CoverageRecord;
+using android::trusty::fuzz::ExtraCounters;
+using android::trusty::fuzz::TrustyApp;
 
 #define TIPC_DEV "/dev/trusty-ipc-dev0"
 #define GATEKEEPER_PORT "com.android.trusty.gatekeeper"
+#define GATEKEEPER_MODULE_NAME "gatekeeper.syms.elf"
+
+/* Gatekeeper TA's UUID is 38ba0cdc-df0e-11e4-9869-233fb6ae4795 */
+static struct uuid gatekeeper_uuid = {
+        0x38ba0cdc,
+        0xdf0e,
+        0x11e4,
+        {0x98, 0x69, 0x23, 0x3f, 0xb6, 0xae, 0x47, 0x95},
+};
+
+static CoverageRecord record(TIPC_DEV, &gatekeeper_uuid, GATEKEEPER_MODULE_NAME);
+
+extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
+    auto ret = record.Open();
+    if (!ret.ok()) {
+        std::cerr << ret.error() << std::endl;
+        exit(-1);
+    }
+    return 0;
+}
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     static uint8_t buf[TIPC_MAX_MSG_SIZE];
 
-    android::trusty::fuzz::TrustyApp ta(TIPC_DEV, GATEKEEPER_PORT);
+    ExtraCounters counters(&record);
+    counters.Reset();
 
+    android::trusty::fuzz::TrustyApp ta(TIPC_DEV, GATEKEEPER_PORT);
     auto ret = ta.Connect();
-    /*
-     * If we can't connect, then assume TA crashed.
-     * TODO: Get some more info, e.g. stacks, to help Haiku dedup crashes.
-     */
     if (!ret.ok()) {
         android::trusty::fuzz::Abort();
     }
diff --git a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
index 98cbcc3..7184e4d 100644
--- a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
+++ b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
@@ -221,10 +221,10 @@
 
 Return<ErrorCode> TrustyKeymaster3Device::addRngEntropy(const hidl_vec<uint8_t>& data) {
     if (data.size() == 0) return ErrorCode::OK;
-    AddEntropyRequest request;
+    AddEntropyRequest request(impl_->message_version());
     request.random_data.Reinitialize(data.data(), data.size());
 
-    AddEntropyResponse response;
+    AddEntropyResponse response(impl_->message_version());
     impl_->AddRngEntropy(request, &response);
 
     return legacy_enum_conversion(response.error);
@@ -232,10 +232,10 @@
 
 Return<void> TrustyKeymaster3Device::generateKey(const hidl_vec<KeyParameter>& keyParams,
                                                  generateKey_cb _hidl_cb) {
-    GenerateKeyRequest request;
+    GenerateKeyRequest request(impl_->message_version());
     request.key_description.Reinitialize(KmParamSet(keyParams));
 
-    GenerateKeyResponse response;
+    GenerateKeyResponse response(impl_->message_version());
     impl_->GenerateKey(request, &response);
 
     KeyCharacteristics resultCharacteristics;
@@ -253,11 +253,11 @@
                                                            const hidl_vec<uint8_t>& clientId,
                                                            const hidl_vec<uint8_t>& appData,
                                                            getKeyCharacteristics_cb _hidl_cb) {
-    GetKeyCharacteristicsRequest request;
+    GetKeyCharacteristicsRequest request(impl_->message_version());
     request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
     addClientAndAppData(clientId, appData, &request.additional_params);
 
-    GetKeyCharacteristicsResponse response;
+    GetKeyCharacteristicsResponse response(impl_->message_version());
     impl_->GetKeyCharacteristics(request, &response);
 
     KeyCharacteristics resultCharacteristics;
@@ -273,12 +273,12 @@
                                                KeyFormat keyFormat,
                                                const hidl_vec<uint8_t>& keyData,
                                                importKey_cb _hidl_cb) {
-    ImportKeyRequest request;
+    ImportKeyRequest request(impl_->message_version());
     request.key_description.Reinitialize(KmParamSet(params));
     request.key_format = legacy_enum_conversion(keyFormat);
     request.SetKeyMaterial(keyData.data(), keyData.size());
 
-    ImportKeyResponse response;
+    ImportKeyResponse response(impl_->message_version());
     impl_->ImportKey(request, &response);
 
     KeyCharacteristics resultCharacteristics;
@@ -297,12 +297,12 @@
                                                const hidl_vec<uint8_t>& clientId,
                                                const hidl_vec<uint8_t>& appData,
                                                exportKey_cb _hidl_cb) {
-    ExportKeyRequest request;
+    ExportKeyRequest request(impl_->message_version());
     request.key_format = legacy_enum_conversion(exportFormat);
     request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
     addClientAndAppData(clientId, appData, &request.additional_params);
 
-    ExportKeyResponse response;
+    ExportKeyResponse response(impl_->message_version());
     impl_->ExportKey(request, &response);
 
     hidl_vec<uint8_t> resultKeyBlob;
@@ -316,11 +316,11 @@
 Return<void> TrustyKeymaster3Device::attestKey(const hidl_vec<uint8_t>& keyToAttest,
                                                const hidl_vec<KeyParameter>& attestParams,
                                                attestKey_cb _hidl_cb) {
-    AttestKeyRequest request;
+    AttestKeyRequest request(impl_->message_version());
     request.SetKeyMaterial(keyToAttest.data(), keyToAttest.size());
     request.attest_params.Reinitialize(KmParamSet(attestParams));
 
-    AttestKeyResponse response;
+    AttestKeyResponse response(impl_->message_version());
     impl_->AttestKey(request, &response);
 
     hidl_vec<hidl_vec<uint8_t>> resultCertChain;
@@ -334,11 +334,11 @@
 Return<void> TrustyKeymaster3Device::upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
                                                 const hidl_vec<KeyParameter>& upgradeParams,
                                                 upgradeKey_cb _hidl_cb) {
-    UpgradeKeyRequest request;
+    UpgradeKeyRequest request(impl_->message_version());
     request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
     request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
 
-    UpgradeKeyResponse response;
+    UpgradeKeyResponse response(impl_->message_version());
     impl_->UpgradeKey(request, &response);
 
     if (response.error == KM_ERROR_OK) {
@@ -350,18 +350,18 @@
 }
 
 Return<ErrorCode> TrustyKeymaster3Device::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
-    DeleteKeyRequest request;
+    DeleteKeyRequest request(impl_->message_version());
     request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
 
-    DeleteKeyResponse response;
+    DeleteKeyResponse response(impl_->message_version());
     impl_->DeleteKey(request, &response);
 
     return legacy_enum_conversion(response.error);
 }
 
 Return<ErrorCode> TrustyKeymaster3Device::deleteAllKeys() {
-    DeleteAllKeysRequest request;
-    DeleteAllKeysResponse response;
+    DeleteAllKeysRequest request(impl_->message_version());
+    DeleteAllKeysResponse response(impl_->message_version());
     impl_->DeleteAllKeys(request, &response);
 
     return legacy_enum_conversion(response.error);
@@ -374,15 +374,15 @@
 Return<void> TrustyKeymaster3Device::begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
                                            const hidl_vec<KeyParameter>& inParams,
                                            begin_cb _hidl_cb) {
-    BeginOperationRequest request;
+    BeginOperationRequest request(impl_->message_version());
     request.purpose = legacy_enum_conversion(purpose);
     request.SetKeyMaterial(key.data(), key.size());
     request.additional_params.Reinitialize(KmParamSet(inParams));
 
-    BeginOperationResponse response;
+    BeginOperationResponse response(impl_->message_version());
     impl_->BeginOperation(request, &response);
 
-    hidl_vec<KeyParameter> resultParams;
+    hidl_vec<KeyParameter> resultParams(impl_->message_version());
     if (response.error == KM_ERROR_OK) {
         resultParams = kmParamSet2Hidl(response.output_params);
     }
@@ -394,8 +394,8 @@
 Return<void> TrustyKeymaster3Device::update(uint64_t operationHandle,
                                             const hidl_vec<KeyParameter>& inParams,
                                             const hidl_vec<uint8_t>& input, update_cb _hidl_cb) {
-    UpdateOperationRequest request;
-    UpdateOperationResponse response;
+    UpdateOperationRequest request(impl_->message_version());
+    UpdateOperationResponse response(impl_->message_version());
     hidl_vec<KeyParameter> resultParams;
     hidl_vec<uint8_t> resultBlob;
     uint32_t resultConsumed = 0;
@@ -431,13 +431,13 @@
                                             const hidl_vec<uint8_t>& input,
                                             const hidl_vec<uint8_t>& signature,
                                             finish_cb _hidl_cb) {
-    FinishOperationRequest request;
+    FinishOperationRequest request(impl_->message_version());
     request.op_handle = operationHandle;
     request.input.Reinitialize(input.data(), input.size());
     request.signature.Reinitialize(signature.data(), signature.size());
     request.additional_params.Reinitialize(KmParamSet(inParams));
 
-    FinishOperationResponse response;
+    FinishOperationResponse response(impl_->message_version());
     impl_->FinishOperation(request, &response);
 
     hidl_vec<KeyParameter> resultParams;
@@ -451,10 +451,10 @@
 }
 
 Return<ErrorCode> TrustyKeymaster3Device::abort(uint64_t operationHandle) {
-    AbortOperationRequest request;
+    AbortOperationRequest request(impl_->message_version());
     request.op_handle = operationHandle;
 
-    AbortOperationResponse response;
+    AbortOperationResponse response(impl_->message_version());
     impl_->AbortOperation(request, &response);
 
     return legacy_enum_conversion(response.error);
diff --git a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
index ec2ba12..73ad6ae 100644
--- a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
+++ b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
@@ -284,7 +284,7 @@
 
 Return<void> TrustyKeymaster4Device::computeSharedHmac(
         const hidl_vec<HmacSharingParameters>& params, computeSharedHmac_cb _hidl_cb) {
-    ComputeSharedHmacRequest request;
+    ComputeSharedHmacRequest request(impl_->message_version());
     request.params_array.params_array = new keymaster::HmacSharingParameters[params.size()];
     request.params_array.num_params = params.size();
     for (size_t i = 0; i < params.size(); ++i) {
@@ -309,7 +309,7 @@
 Return<void> TrustyKeymaster4Device::verifyAuthorization(
         uint64_t challenge, const hidl_vec<KeyParameter>& parametersToVerify,
         const HardwareAuthToken& authToken, verifyAuthorization_cb _hidl_cb) {
-    VerifyAuthorizationRequest request;
+    VerifyAuthorizationRequest request(impl_->message_version());
     request.challenge = challenge;
     request.parameters_to_verify.Reinitialize(KmParamSet(parametersToVerify));
     request.auth_token.challenge = authToken.challenge;
@@ -336,10 +336,10 @@
 
 Return<ErrorCode> TrustyKeymaster4Device::addRngEntropy(const hidl_vec<uint8_t>& data) {
     if (data.size() == 0) return ErrorCode::OK;
-    AddEntropyRequest request;
+    AddEntropyRequest request(impl_->message_version());
     request.random_data.Reinitialize(data.data(), data.size());
 
-    AddEntropyResponse response;
+    AddEntropyResponse response(impl_->message_version());
     impl_->AddRngEntropy(request, &response);
 
     return legacy_enum_conversion(response.error);
@@ -347,10 +347,10 @@
 
 Return<void> TrustyKeymaster4Device::generateKey(const hidl_vec<KeyParameter>& keyParams,
                                                  generateKey_cb _hidl_cb) {
-    GenerateKeyRequest request;
+    GenerateKeyRequest request(impl_->message_version());
     request.key_description.Reinitialize(KmParamSet(keyParams));
 
-    GenerateKeyResponse response;
+    GenerateKeyResponse response(impl_->message_version());
     impl_->GenerateKey(request, &response);
 
     KeyCharacteristics resultCharacteristics;
@@ -368,11 +368,11 @@
                                                            const hidl_vec<uint8_t>& clientId,
                                                            const hidl_vec<uint8_t>& appData,
                                                            getKeyCharacteristics_cb _hidl_cb) {
-    GetKeyCharacteristicsRequest request;
+    GetKeyCharacteristicsRequest request(impl_->message_version());
     request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
     addClientAndAppData(clientId, appData, &request.additional_params);
 
-    GetKeyCharacteristicsResponse response;
+    GetKeyCharacteristicsResponse response(impl_->message_version());
     impl_->GetKeyCharacteristics(request, &response);
 
     KeyCharacteristics resultCharacteristics;
@@ -388,12 +388,12 @@
                                                KeyFormat keyFormat,
                                                const hidl_vec<uint8_t>& keyData,
                                                importKey_cb _hidl_cb) {
-    ImportKeyRequest request;
+    ImportKeyRequest request(impl_->message_version());
     request.key_description.Reinitialize(KmParamSet(params));
     request.key_format = legacy_enum_conversion(keyFormat);
     request.SetKeyMaterial(keyData.data(), keyData.size());
 
-    ImportKeyResponse response;
+    ImportKeyResponse response(impl_->message_version());
     impl_->ImportKey(request, &response);
 
     KeyCharacteristics resultCharacteristics;
@@ -411,7 +411,7 @@
         const hidl_vec<uint8_t>& wrappedKeyData, const hidl_vec<uint8_t>& wrappingKeyBlob,
         const hidl_vec<uint8_t>& maskingKey, const hidl_vec<KeyParameter>& unwrappingParams,
         uint64_t passwordSid, uint64_t biometricSid, importWrappedKey_cb _hidl_cb) {
-    ImportWrappedKeyRequest request;
+    ImportWrappedKeyRequest request(impl_->message_version());
     request.SetWrappedMaterial(wrappedKeyData.data(), wrappedKeyData.size());
     request.SetWrappingMaterial(wrappingKeyBlob.data(), wrappingKeyBlob.size());
     request.SetMaskingKeyMaterial(maskingKey.data(), maskingKey.size());
@@ -419,7 +419,7 @@
     request.password_sid = passwordSid;
     request.biometric_sid = biometricSid;
 
-    ImportWrappedKeyResponse response;
+    ImportWrappedKeyResponse response(impl_->message_version());
     impl_->ImportWrappedKey(request, &response);
 
     KeyCharacteristics resultCharacteristics;
@@ -438,12 +438,12 @@
                                                const hidl_vec<uint8_t>& clientId,
                                                const hidl_vec<uint8_t>& appData,
                                                exportKey_cb _hidl_cb) {
-    ExportKeyRequest request;
+    ExportKeyRequest request(impl_->message_version());
     request.key_format = legacy_enum_conversion(exportFormat);
     request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
     addClientAndAppData(clientId, appData, &request.additional_params);
 
-    ExportKeyResponse response;
+    ExportKeyResponse response(impl_->message_version());
     impl_->ExportKey(request, &response);
 
     hidl_vec<uint8_t> resultKeyBlob;
@@ -457,11 +457,11 @@
 Return<void> TrustyKeymaster4Device::attestKey(const hidl_vec<uint8_t>& keyToAttest,
                                                const hidl_vec<KeyParameter>& attestParams,
                                                attestKey_cb _hidl_cb) {
-    AttestKeyRequest request;
+    AttestKeyRequest request(impl_->message_version());
     request.SetKeyMaterial(keyToAttest.data(), keyToAttest.size());
     request.attest_params.Reinitialize(KmParamSet(attestParams));
 
-    AttestKeyResponse response;
+    AttestKeyResponse response(impl_->message_version());
     impl_->AttestKey(request, &response);
 
     hidl_vec<hidl_vec<uint8_t>> resultCertChain;
@@ -475,11 +475,11 @@
 Return<void> TrustyKeymaster4Device::upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
                                                 const hidl_vec<KeyParameter>& upgradeParams,
                                                 upgradeKey_cb _hidl_cb) {
-    UpgradeKeyRequest request;
+    UpgradeKeyRequest request(impl_->message_version());
     request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
     request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
 
-    UpgradeKeyResponse response;
+    UpgradeKeyResponse response(impl_->message_version());
     impl_->UpgradeKey(request, &response);
 
     if (response.error == KM_ERROR_OK) {
@@ -491,18 +491,18 @@
 }
 
 Return<ErrorCode> TrustyKeymaster4Device::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
-    DeleteKeyRequest request;
+    DeleteKeyRequest request(impl_->message_version());
     request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
 
-    DeleteKeyResponse response;
+    DeleteKeyResponse response(impl_->message_version());
     impl_->DeleteKey(request, &response);
 
     return legacy_enum_conversion(response.error);
 }
 
 Return<ErrorCode> TrustyKeymaster4Device::deleteAllKeys() {
-    DeleteAllKeysRequest request;
-    DeleteAllKeysResponse response;
+    DeleteAllKeysRequest request(impl_->message_version());
+    DeleteAllKeysResponse response(impl_->message_version());
     impl_->DeleteAllKeys(request, &response);
 
     return legacy_enum_conversion(response.error);
@@ -516,12 +516,12 @@
                                            const hidl_vec<KeyParameter>& inParams,
                                            const HardwareAuthToken& authToken, begin_cb _hidl_cb) {
     hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
-    BeginOperationRequest request;
+    BeginOperationRequest request(impl_->message_version());
     request.purpose = legacy_enum_conversion(purpose);
     request.SetKeyMaterial(key.data(), key.size());
     request.additional_params.Reinitialize(KmParamSet(extendedParams));
 
-    BeginOperationResponse response;
+    BeginOperationResponse response(impl_->message_version());
     impl_->BeginOperation(request, &response);
 
     hidl_vec<KeyParameter> resultParams;
@@ -540,8 +540,8 @@
                                             const VerificationToken& verificationToken,
                                             update_cb _hidl_cb) {
     (void)verificationToken;
-    UpdateOperationRequest request;
-    UpdateOperationResponse response;
+    UpdateOperationRequest request(impl_->message_version());
+    UpdateOperationResponse response(impl_->message_version());
     hidl_vec<KeyParameter> resultParams;
     hidl_vec<uint8_t> resultBlob;
     hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
@@ -581,14 +581,14 @@
                                             const VerificationToken& verificationToken,
                                             finish_cb _hidl_cb) {
     (void)verificationToken;
-    FinishOperationRequest request;
+    FinishOperationRequest request(impl_->message_version());
     hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
     request.op_handle = operationHandle;
     request.input.Reinitialize(input.data(), input.size());
     request.signature.Reinitialize(signature.data(), signature.size());
     request.additional_params.Reinitialize(KmParamSet(extendedParams));
 
-    FinishOperationResponse response;
+    FinishOperationResponse response(impl_->message_version());
     impl_->FinishOperation(request, &response);
 
     hidl_vec<KeyParameter> resultParams;
@@ -602,10 +602,10 @@
 }
 
 Return<ErrorCode> TrustyKeymaster4Device::abort(uint64_t operationHandle) {
-    AbortOperationRequest request;
+    AbortOperationRequest request(impl_->message_version());
     request.op_handle = operationHandle;
 
-    AbortOperationResponse response;
+    AbortOperationResponse response(impl_->message_version());
     impl_->AbortOperation(request, &response);
 
     return legacy_enum_conversion(response.error);
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index 750a9d7..23e0433 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -31,11 +31,41 @@
         return err;
     }
 
-    ConfigureRequest req;
+    // Try GetVersion2 first.
+    GetVersion2Request versionReq;
+    GetVersion2Response versionRsp = GetVersion2(versionReq);
+    if (versionRsp.error != KM_ERROR_OK) {
+        ALOGW("TA appears not to support GetVersion2, falling back (err = %d)", versionRsp.error);
+
+        err = trusty_keymaster_connect();
+        if (err) {
+            ALOGE("Failed to connect to trusty keymaster %d", err);
+            return err;
+        }
+
+        GetVersionRequest versionReq;
+        GetVersionResponse versionRsp;
+        GetVersion(versionReq, &versionRsp);
+        if (versionRsp.error != KM_ERROR_OK) {
+            ALOGE("Failed to get TA version %d", versionRsp.error);
+            return -1;
+        } else {
+            keymaster_error_t error;
+            message_version_ = NegotiateMessageVersion(versionRsp, &error);
+            if (error != KM_ERROR_OK) {
+                ALOGE("Failed to negotiate message version %d", error);
+                return -1;
+            }
+        }
+    } else {
+        message_version_ = NegotiateMessageVersion(versionReq, versionRsp);
+    }
+
+    ConfigureRequest req(message_version());
     req.os_version = GetOsVersion();
     req.os_patchlevel = GetOsPatchlevel();
 
-    ConfigureResponse rsp;
+    ConfigureResponse rsp(message_version());
     Configure(req, &rsp);
 
     if (rsp.error != KM_ERROR_OK) {
@@ -52,7 +82,7 @@
     trusty_keymaster_disconnect();
 }
 
-static void ForwardCommand(enum keymaster_command command, const Serializable& req,
+static void ForwardCommand(enum keymaster_command command, const KeymasterMessage& req,
                            KeymasterResponse* rsp) {
     keymaster_error_t err;
     err = trusty_keymaster_send(command, req, rsp);
@@ -173,25 +203,30 @@
 }
 
 GetHmacSharingParametersResponse TrustyKeymaster::GetHmacSharingParameters() {
-    // Empty buffer to allow ForwardCommand to have something to serialize
-    Buffer request;
-    GetHmacSharingParametersResponse response;
+    GetHmacSharingParametersRequest request(message_version());
+    GetHmacSharingParametersResponse response(message_version());
     ForwardCommand(KM_GET_HMAC_SHARING_PARAMETERS, request, &response);
     return response;
 }
 
 ComputeSharedHmacResponse TrustyKeymaster::ComputeSharedHmac(
         const ComputeSharedHmacRequest& request) {
-    ComputeSharedHmacResponse response;
+    ComputeSharedHmacResponse response(message_version());
     ForwardCommand(KM_COMPUTE_SHARED_HMAC, request, &response);
     return response;
 }
 
 VerifyAuthorizationResponse TrustyKeymaster::VerifyAuthorization(
         const VerifyAuthorizationRequest& request) {
-    VerifyAuthorizationResponse response;
+    VerifyAuthorizationResponse response(message_version());
     ForwardCommand(KM_VERIFY_AUTHORIZATION, request, &response);
     return response;
 }
 
+GetVersion2Response TrustyKeymaster::GetVersion2(const GetVersion2Request& request) {
+    GetVersion2Response response(message_version());
+    ForwardCommand(KM_GET_VERSION_2, request, &response);
+    return response;
+}
+
 }  // namespace keymaster
diff --git a/deprecated-adf/libadf/tests/Android.bp b/trusty/keymaster/fuzz/Android.bp
similarity index 66%
copy from deprecated-adf/libadf/tests/Android.bp
copy to trusty/keymaster/fuzz/Android.bp
index 9b3430e..da9f9ec 100644
--- a/deprecated-adf/libadf/tests/Android.bp
+++ b/trusty/keymaster/fuzz/Android.bp
@@ -1,23 +1,19 @@
-//
-// Copyright (C) 2013 The Android Open Source Project
+// Copyright (C) 2020 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
+//       http://www.apache.org/licenses/LICENSE-2.0
 //
 // Unless required by applicable law or agreed to in writing, software
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-//
 
-cc_test {
-    name: "adf-unit-tests",
-    srcs: ["adf_test.cpp"],
-    shared_libs: ["libsync"],
-    static_libs: ["libadf"],
-    cflags: ["-Werror"],
+cc_fuzz {
+    name: "trusty_keymaster_fuzzer",
+    defaults: ["trusty_fuzzer_defaults"],
+    srcs: ["fuzz.cpp"],
 }
diff --git a/trusty/keymaster/fuzz/fuzz.cpp b/trusty/keymaster/fuzz/fuzz.cpp
new file mode 100644
index 0000000..4ac97bb
--- /dev/null
+++ b/trusty/keymaster/fuzz/fuzz.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 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 <stdlib.h>
+#include <trusty/coverage/coverage.h>
+#include <trusty/fuzz/counters.h>
+#include <trusty/fuzz/utils.h>
+#include <unistd.h>
+#include <iostream>
+
+using android::trusty::coverage::CoverageRecord;
+using android::trusty::fuzz::ExtraCounters;
+using android::trusty::fuzz::TrustyApp;
+
+#define TIPC_DEV "/dev/trusty-ipc-dev0"
+#define KEYMASTER_PORT "com.android.trusty.keymaster"
+#define KEYMASTER_MODULE_FILENAME "keymaster.syms.elf"
+
+/* Keymaster TA's UUID is 5f902ace-5e5c-4cd8-ae54-87b88c22ddaf */
+static struct uuid keymaster_uuid = {
+        0x5f902ace,
+        0x5e5c,
+        0x4cd8,
+        {0xae, 0x54, 0x87, 0xb8, 0x8c, 0x22, 0xdd, 0xaf},
+};
+
+static CoverageRecord record(TIPC_DEV, &keymaster_uuid, KEYMASTER_MODULE_FILENAME);
+
+extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
+    auto ret = record.Open();
+    if (!ret.ok()) {
+        std::cerr << ret.error() << std::endl;
+        exit(-1);
+    }
+    return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    static uint8_t buf[TIPC_MAX_MSG_SIZE];
+
+    ExtraCounters counters(&record);
+    counters.Reset();
+
+    android::trusty::fuzz::TrustyApp ta(TIPC_DEV, KEYMASTER_PORT);
+    auto ret = ta.Connect();
+    if (!ret.ok()) {
+        android::trusty::fuzz::Abort();
+    }
+
+    /* Send message to test server */
+    ret = ta.Write(data, size);
+    if (!ret.ok()) {
+        return -1;
+    }
+
+    /* Read message from test server */
+    ret = ta.Read(&buf, sizeof(buf));
+    if (!ret.ok()) {
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
index 030b645..bec2a2a 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
@@ -59,6 +59,12 @@
     GetHmacSharingParametersResponse GetHmacSharingParameters();
     ComputeSharedHmacResponse ComputeSharedHmac(const ComputeSharedHmacRequest& request);
     VerifyAuthorizationResponse VerifyAuthorization(const VerifyAuthorizationRequest& request);
+    GetVersion2Response GetVersion2(const GetVersion2Request& request);
+
+    uint32_t message_version() const { return message_version_; }
+
+  private:
+    uint32_t message_version_;
 };
 
 }  // namespace keymaster
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
index ce2cc2e..419c96f 100644
--- a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
@@ -53,6 +53,7 @@
     KM_DELETE_ALL_KEYS              = (23 << KEYMASTER_REQ_SHIFT),
     KM_DESTROY_ATTESTATION_IDS      = (24 << KEYMASTER_REQ_SHIFT),
     KM_IMPORT_WRAPPED_KEY           = (25 << KEYMASTER_REQ_SHIFT),
+    KM_GET_VERSION_2                = (28 << KEYMASTER_REQ_SHIFT),
 
     // Bootloader/provisioning calls.
     KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT),
diff --git a/trusty/keymaster/set_attestation_key/set_attestation_key.cpp b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
index 6f74833..df6b0f8 100644
--- a/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
+++ b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
@@ -70,7 +70,7 @@
 }
 
 struct SetAttestationKeyRequest : public keymaster::KeymasterMessage {
-    explicit SetAttestationKeyRequest(int32_t ver = keymaster::MAX_MESSAGE_VERSION)
+    explicit SetAttestationKeyRequest(int32_t ver = keymaster::kDefaultMessageVersion)
         : KeymasterMessage(ver) {}
 
     size_t SerializedSize() const override { return sizeof(uint32_t) + key_data.SerializedSize(); }
@@ -88,7 +88,7 @@
 };
 
 struct KeymasterNoResponse : public keymaster::KeymasterResponse {
-    explicit KeymasterNoResponse(int32_t ver = keymaster::MAX_MESSAGE_VERSION)
+    explicit KeymasterNoResponse(int32_t ver = keymaster::kDefaultMessageVersion)
         : keymaster::KeymasterResponse(ver) {}
 
     size_t NonErrorSerializedSize() const override { return 0; }
@@ -99,7 +99,7 @@
 struct SetAttestationKeyResponse : public KeymasterNoResponse {};
 
 struct ClearAttestationCertChainRequest : public keymaster::KeymasterMessage {
-    explicit ClearAttestationCertChainRequest(int32_t ver = keymaster::MAX_MESSAGE_VERSION)
+    explicit ClearAttestationCertChainRequest(int32_t ver = keymaster::kDefaultMessageVersion)
         : KeymasterMessage(ver) {}
 
     size_t SerializedSize() const override { return sizeof(uint32_t); }
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp
index 8dba78d..e0161a5 100644
--- a/trusty/libtrusty/Android.bp
+++ b/trusty/libtrusty/Android.bp
@@ -12,10 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_library {
-    name: "libtrusty",
-    vendor: true,
-
+cc_defaults {
+    name: "libtrusty_defaults",
     srcs: ["trusty.c"],
     export_include_dirs: ["include"],
     cflags: [
@@ -25,3 +23,11 @@
 
     shared_libs: ["liblog"],
 }
+
+cc_library {
+    name: "libtrusty",
+    // TODO(b/170753563): cc_fuzz can't deal with vendor components. Build
+    // libtrusty for system and vendor.
+    vendor_available: true,
+    defaults: ["libtrusty_defaults"],
+}
diff --git a/trusty/libtrusty/tipc-test/Android.bp b/trusty/libtrusty/tipc-test/Android.bp
index 9676b79..5e60d28 100644
--- a/trusty/libtrusty/tipc-test/Android.bp
+++ b/trusty/libtrusty/tipc-test/Android.bp
@@ -19,6 +19,7 @@
     srcs: ["tipc_test.c"],
     shared_libs: [
         "libc",
+        "libdmabufheap",
         "liblog",
         "libtrusty",
     ],
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index ca581dc..94aedd7 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -25,6 +25,8 @@
 #include <sys/mman.h>
 #include <sys/uio.h>
 
+#include <BufferAllocator/BufferAllocatorWrapper.h>
+
 #include <trusty/tipc.h>
 
 #define TIPC_DEFAULT_DEVNAME "/dev/trusty-ipc-dev0"
@@ -86,7 +88,7 @@
         "   ta-access    - test ta-access flags\n"
         "   writev       - writev test\n"
         "   readv        - readv test\n"
-        "   send-fd      - transmit memfd to trusty, use as shm\n"
+        "   send-fd      - transmit dma_buf to trusty, use as shm\n"
         "\n";
 
 static uint opt_repeat  = 1;
@@ -890,9 +892,12 @@
 
 static int send_fd_test(void) {
     int ret;
-    int memfd = -1;
+    int dma_buf = -1;
     int fd = -1;
     volatile char* buf = MAP_FAILED;
+    BufferAllocator* allocator = NULL;
+
+    const size_t num_pages = 10;
 
     fd = tipc_connect(dev_name, receiver_name);
     if (fd < 0) {
@@ -901,22 +906,24 @@
         goto cleanup;
     }
 
-    memfd = memfd_create("tipc-send-fd", 0);
-    if (memfd < 0) {
-        fprintf(stderr, "Failed to create memfd: %s\n", strerror(errno));
+    allocator = CreateDmabufHeapBufferAllocator();
+    if (!allocator) {
+        fprintf(stderr, "Failed to create dma-buf allocator.\n");
         ret = -1;
         goto cleanup;
     }
 
-    if (ftruncate(memfd, PAGE_SIZE) < 0) {
-        fprintf(stderr, "Failed to resize memfd: %s\n", strerror(errno));
-        ret = -1;
+    size_t buf_size = PAGE_SIZE * num_pages;
+    dma_buf = DmabufHeapAlloc(allocator, "system", buf_size, 0);
+    if (dma_buf < 0) {
+        ret = dma_buf;
+        fprintf(stderr, "Failed to create dma-buf fd of size %zu err (%d)\n", buf_size, ret);
         goto cleanup;
     }
 
-    buf = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, 0);
+    buf = mmap(0, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
     if (buf == MAP_FAILED) {
-        fprintf(stderr, "Failed to map memfd: %s\n", strerror(errno));
+        fprintf(stderr, "Failed to map dma-buf: %s\n", strerror(errno));
         ret = -1;
         goto cleanup;
     }
@@ -924,13 +931,13 @@
     strcpy((char*)buf, "From NS");
 
     struct trusty_shm shm = {
-            .fd = memfd,
+            .fd = dma_buf,
             .transfer = TRUSTY_SHARE,
     };
 
     ssize_t rc = tipc_send(fd, NULL, 0, &shm, 1);
     if (rc < 0) {
-        fprintf(stderr, "tipc_send failed\n");
+        fprintf(stderr, "tipc_send failed: %zd\n", rc);
         ret = rc;
         goto cleanup;
     }
@@ -938,13 +945,19 @@
     read(fd, &c, 1);
     tipc_close(fd);
 
-    ret = strcmp("Hello from Trusty!", (const char*)buf) ? (-1) : 0;
+    ret = 0;
+    for (size_t skip = 0; skip < num_pages; skip++) {
+        ret |= strcmp("Hello from Trusty!", (const char*)&buf[skip * PAGE_SIZE]) ? (-1) : 0;
+    }
 
 cleanup:
     if (buf != MAP_FAILED) {
         munmap((char*)buf, PAGE_SIZE);
     }
-    close(memfd);
+    close(dma_buf);
+    if (allocator) {
+        FreeDmabufHeapBufferAllocator(allocator);
+    }
     tipc_close(fd);
     return ret;
 }
diff --git a/trusty/libtrusty/trusty.c b/trusty/libtrusty/trusty.c
index ad4d8cd..f44f8b4 100644
--- a/trusty/libtrusty/trusty.c
+++ b/trusty/libtrusty/trusty.c
@@ -29,30 +29,27 @@
 
 #include <trusty/ipc.h>
 
-int tipc_connect(const char *dev_name, const char *srv_name)
-{
-	int fd;
-	int rc;
+int tipc_connect(const char* dev_name, const char* srv_name) {
+    int fd;
+    int rc;
 
-	fd = open(dev_name, O_RDWR);
-	if (fd < 0) {
-		rc = -errno;
-		ALOGE("%s: cannot open tipc device \"%s\": %s\n",
-		      __func__, dev_name, strerror(errno));
-		return rc < 0 ? rc : -1;
-	}
+    fd = TEMP_FAILURE_RETRY(open(dev_name, O_RDWR));
+    if (fd < 0) {
+        rc = -errno;
+        ALOGE("%s: cannot open tipc device \"%s\": %s\n", __func__, dev_name, strerror(errno));
+        return rc < 0 ? rc : -1;
+    }
 
-	rc = ioctl(fd, TIPC_IOC_CONNECT, srv_name);
-	if (rc < 0) {
-		rc = -errno;
-		ALOGE("%s: can't connect to tipc service \"%s\" (err=%d)\n",
-		      __func__, srv_name, errno);
-		close(fd);
-		return rc < 0 ? rc : -1;
-	}
+    rc = TEMP_FAILURE_RETRY(ioctl(fd, TIPC_IOC_CONNECT, srv_name));
+    if (rc < 0) {
+        rc = -errno;
+        ALOGE("%s: can't connect to tipc service \"%s\" (err=%d)\n", __func__, srv_name, errno);
+        close(fd);
+        return rc < 0 ? rc : -1;
+    }
 
-	ALOGV("%s: connected to \"%s\" fd %d\n", __func__, srv_name, fd);
-	return fd;
+    ALOGV("%s: connected to \"%s\" fd %d\n", __func__, srv_name, fd);
+    return fd;
 }
 
 ssize_t tipc_send(int fd, const struct iovec* iov, int iovcnt, struct trusty_shm* shms,
@@ -63,7 +60,7 @@
     req.shm = (__u64)shms;
     req.shm_cnt = (__u64)shmcnt;
 
-    int rc = ioctl(fd, TIPC_IOC_SEND_MSG, &req);
+    int rc = TEMP_FAILURE_RETRY(ioctl(fd, TIPC_IOC_SEND_MSG, &req));
     if (rc < 0) {
         ALOGE("%s: failed to send message (err=%d)\n", __func__, rc);
     }
@@ -71,7 +68,6 @@
     return rc;
 }
 
-void tipc_close(int fd)
-{
-	close(fd);
+void tipc_close(int fd) {
+    close(fd);
 }
diff --git a/deprecated-adf/libadf/tests/Android.bp b/trusty/secure_dpu/Android.bp
similarity index 72%
rename from deprecated-adf/libadf/tests/Android.bp
rename to trusty/secure_dpu/Android.bp
index 9b3430e..0d57cea 100644
--- a/deprecated-adf/libadf/tests/Android.bp
+++ b/trusty/secure_dpu/Android.bp
@@ -1,5 +1,4 @@
-//
-// Copyright (C) 2013 The Android Open Source Project
+// Copyright (C) 2021 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,12 +11,10 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-//
 
-cc_test {
-    name: "adf-unit-tests",
-    srcs: ["adf_test.cpp"],
-    shared_libs: ["libsync"],
-    static_libs: ["libadf"],
-    cflags: ["-Werror"],
+cc_library_headers {
+    name: "secure_dpu_headers",
+    vendor: true,
+
+    export_include_dirs: ["include"],
 }
diff --git a/trusty/secure_dpu/include/trusty/secure_dpu/SecureDpu.h b/trusty/secure_dpu/include/trusty/secure_dpu/SecureDpu.h
new file mode 100644
index 0000000..b939d44
--- /dev/null
+++ b/trusty/secure_dpu/include/trusty/secure_dpu/SecureDpu.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/**
+ * DOC: Secure DPU
+ *
+ * The Secure DPU works as the persistent channel between the non-secure and the
+ * secure world. The channel is established during the boot up stage of the
+ * non-secure world system. In general, the established channel allows the
+ * secure world applications initiate requests or notifications to the non-secure
+ * world.
+ *
+ * For particular devices, the secure world can only perform operations on the
+ * display when in the TUI session if device-specific setup is done by the
+ * non-secure world. Besides, the non-secure world could allocate framebuffer
+ * for the secure world application if the memory is limited in the secure world
+ * on specific devices.
+ *
+ * Currently, supported requests are to start / stop the secure display mode and
+ * to allocate framebuffer.
+ *
+ * This header file needs to be synced on both the Trusty and the Android
+ * codebase.
+ */
+
+#define SECURE_DPU_PORT_NAME "com.android.trusty.secure_dpu"
+#define SECURE_DPU_MAX_MSG_SIZE 64
+
+/**
+ * enum secure_dpu_cmd - command identifiers for secure_fb interface
+ * @SECURE_DPU_CMD_RESP_BIT:
+ *      Message is a response.
+ * @SECURE_DPU_CMD_REQ_SHIFT:
+ *      Number of bits used by @SECURE_DPU_CMD_RESP_BIT.
+ * @SECURE_DPU_CMD_START_SECURE_DISPLAY:
+ *      Notify the system to start secure display mode
+ * @SECURE_DPU_CMD_STOP_SECURE_DISPLAY:
+ *      Notify the system to stop secure display mode
+ * @SECURE_DPU_CMD_ALLOCATE_BUFFER:
+ *      Request non-secure world to allocate the buffer
+ */
+enum secure_dpu_cmd {
+    SECURE_DPU_CMD_RESP_BIT = 1,
+    SECURE_DPU_CMD_REQ_SHIFT = 1,
+    SECURE_DPU_CMD_START_SECURE_DISPLAY = (1 << SECURE_DPU_CMD_REQ_SHIFT),
+    SECURE_DPU_CMD_STOP_SECURE_DISPLAY = (2 << SECURE_DPU_CMD_REQ_SHIFT),
+    SECURE_DPU_CMD_ALLOCATE_BUFFER = (3 << SECURE_DPU_CMD_REQ_SHIFT),
+};
+
+/**
+ * struct secure_dpu_allocate_buffer_req - payload for
+ *                                         %SECURE_DPU_CMD_ALLOCATE_BUFFER
+ *                                         request
+ * @buffer_len: Requested length
+ */
+struct secure_dpu_allocate_buffer_req {
+    uint64_t buffer_len;
+};
+
+/**
+ * struct secure_dpu_allocate_buffer_resp - payload for
+ *                                          %SECURE_DPU_CMD_ALLOCATE_BUFFER
+ *                                          response
+ * @buffer_len: Allocated length
+ */
+struct secure_dpu_allocate_buffer_resp {
+    uint64_t buffer_len;
+};
+
+/**
+ * struct secure_fb_req - common structure for secure_fb requests.
+ * @cmd: Command identifier - one of &enum secure_dpu_cmd.
+ */
+struct secure_dpu_req {
+    uint32_t cmd;
+};
+
+/**
+ * struct secure_dpu_resp - common structure for secure_dpu responses.
+ * @cmd:    Command identifier - %SECURE_DPU_CMD_RESP_BIT or'ed with the
+ *                               command identifier of the corresponding
+ *                               request.
+ * @status: Status of requested operation. One of &enum secure_dpu_error.
+ */
+struct secure_dpu_resp {
+    uint32_t cmd;
+    int32_t status;
+};
+
+enum secure_dpu_error {
+    SECURE_DPU_ERROR_OK = 0,
+    SECURE_DPU_ERROR_FAIL = -1,
+    SECURE_DPU_ERROR_UNINITIALIZED = -2,
+    SECURE_DPU_ERROR_PARAMETERS = -3,
+    SECURE_DPU_ERROR_NO_MEMORY = -4,
+};
diff --git a/trusty/trusty-test.mk b/trusty/trusty-test.mk
index dc4c962..74106ec 100644
--- a/trusty/trusty-test.mk
+++ b/trusty/trusty-test.mk
@@ -15,4 +15,5 @@
 PRODUCT_PACKAGES += \
 	spiproxyd \
 	trusty_keymaster_set_attestation_key \
-	keymaster_soft_attestation_keys.xml \
\ No newline at end of file
+	keymaster_soft_attestation_keys.xml \
+
diff --git a/trusty/utils/trusty-ut-ctrl/Android.bp b/trusty/utils/trusty-ut-ctrl/Android.bp
index 9c8af7b..664696a 100644
--- a/trusty/utils/trusty-ut-ctrl/Android.bp
+++ b/trusty/utils/trusty-ut-ctrl/Android.bp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_test {
+cc_binary {
     name: "trusty-ut-ctrl",
     vendor: true,
 
@@ -24,7 +24,6 @@
     static_libs: [
         "libtrusty",
     ],
-    gtest: false,
     cflags: [
         "-Wall",
         "-Werror",