Merge "libsparse: use memory-mapping on Windows too."
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 9b6213a..a2ed205 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -28,9 +28,6 @@
       "name": "fs_mgr_vendor_overlay_test"
     },
     {
-      "name": "init_kill_services_test"
-    },
-    {
       "name": "libbase_test"
     },
     {
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index 31c938c..a308732 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -417,17 +417,19 @@
 }
 
 const std::optional<FeatureSet>& adb_get_feature_set(std::string* error) {
-    static std::string cached_error [[clang::no_destroy]];
-    static const std::optional<FeatureSet> features
-            [[clang::no_destroy]] ([]() -> std::optional<FeatureSet> {
-                std::string result;
-                if (adb_query(format_host_command("features"), &result, &cached_error)) {
-                    return StringToFeatureSet(result);
-                }
-                return std::nullopt;
-            }());
-    if (!features && error) {
-        *error = cached_error;
+    static std::mutex feature_mutex [[clang::no_destroy]];
+    static std::optional<FeatureSet> features [[clang::no_destroy]] GUARDED_BY(feature_mutex);
+    std::lock_guard<std::mutex> lock(feature_mutex);
+    if (!features) {
+        std::string result;
+        std::string err;
+        if (adb_query(format_host_command("features"), &result, &err)) {
+            features = StringToFeatureSet(result);
+        } else {
+            if (error) {
+                *error = err;
+            }
+        }
     }
     return features;
 }
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index e562f8b..d66d400 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -310,7 +310,7 @@
     const auto start = clock::now();
     int first_apk = -1;
     int last_apk = -1;
-    std::vector<std::string_view> args = {"package"sv};
+    incremental::Args passthrough_args = {};
     for (int i = 0; i < argc; ++i) {
         const auto arg = std::string_view(argv[i]);
         if (android::base::EndsWithIgnoreCase(arg, ".apk"sv)) {
@@ -318,12 +318,11 @@
             if (first_apk == -1) {
                 first_apk = i;
             }
-        } else if (arg.starts_with("install-"sv)) {
+        } else if (arg.starts_with("install"sv)) {
             // incremental installation command on the device is the same for all its variations in
             // the adb, e.g. install-multiple or install-multi-package
-            args.push_back("install"sv);
         } else {
-            args.push_back(arg);
+            passthrough_args.push_back(arg);
         }
     }
 
@@ -344,7 +343,7 @@
     }
 
     printf("Performing Incremental Install\n");
-    auto server_process = incremental::install(files, silent);
+    auto server_process = incremental::install(files, passthrough_args, silent);
     if (!server_process) {
         return -1;
     }
diff --git a/adb/client/incremental.cpp b/adb/client/incremental.cpp
index 2814932..a8b0ab3 100644
--- a/adb/client/incremental.cpp
+++ b/adb/client/incremental.cpp
@@ -93,12 +93,10 @@
 
 // Send install-incremental to the device along with properly configured file descriptors in
 // streaming format. Once connection established, send all fs-verity tree bytes.
-static unique_fd start_install(const Files& files, bool silent) {
+static unique_fd start_install(const Files& files, const Args& passthrough_args, bool silent) {
     std::vector<std::string> command_args{"package", "install-incremental"};
+    command_args.insert(command_args.end(), passthrough_args.begin(), passthrough_args.end());
 
-    // fd's with positions at the beginning of fs-verity
-    std::vector<unique_fd> signature_fds;
-    signature_fds.reserve(files.size());
     for (int i = 0, size = files.size(); i < size; ++i) {
         const auto& file = files[i];
 
@@ -118,8 +116,6 @@
         auto file_desc = StringPrintf("%s:%lld:%d:%s:1", android::base::Basename(file).c_str(),
                                       (long long)st.st_size, i, signature.c_str());
         command_args.push_back(std::move(file_desc));
-
-        signature_fds.push_back(std::move(signature_fd));
     }
 
     std::string error;
@@ -150,8 +146,8 @@
     return true;
 }
 
-std::optional<Process> install(const Files& files, bool silent) {
-    auto connection_fd = start_install(files, silent);
+std::optional<Process> install(const Files& files, const Args& passthrough_args, bool silent) {
+    auto connection_fd = start_install(files, passthrough_args, silent);
     if (connection_fd < 0) {
         if (!silent) {
             fprintf(stderr, "adb: failed to initiate installation on device.\n");
diff --git a/adb/client/incremental.h b/adb/client/incremental.h
index 1fb1e0b..40e928a 100644
--- a/adb/client/incremental.h
+++ b/adb/client/incremental.h
@@ -26,9 +26,10 @@
 namespace incremental {
 
 using Files = std::vector<std::string>;
+using Args = std::vector<std::string_view>;
 
 bool can_install(const Files& files);
-std::optional<Process> install(const Files& files, bool silent);
+std::optional<Process> install(const Files& files, const Args& passthrough_args, bool silent);
 
 enum class Result { Success, Failure, None };
 Result wait_for_installation(int read_fd);
diff --git a/adb/fdevent/fdevent.h b/adb/fdevent/fdevent.h
index 9fc3b2c..bb3af74 100644
--- a/adb/fdevent/fdevent.h
+++ b/adb/fdevent/fdevent.h
@@ -79,8 +79,8 @@
     unique_fd Destroy(fdevent* fde);
 
   protected:
-    virtual void Register(fdevent*) {}
-    virtual void Unregister(fdevent*) {}
+    virtual void Register(fdevent*) = 0;
+    virtual void Unregister(fdevent*) = 0;
 
   public:
     // Change which events should cause notifications.
diff --git a/adb/fdevent/fdevent_poll.cpp b/adb/fdevent/fdevent_poll.cpp
index ac86c08..21c1ba0 100644
--- a/adb/fdevent/fdevent_poll.cpp
+++ b/adb/fdevent/fdevent_poll.cpp
@@ -211,3 +211,7 @@
         PLOG(FATAL) << "failed to write to fdevent interrupt fd";
     }
 }
+
+void fdevent_context_poll::Register(fdevent*) {}
+
+void fdevent_context_poll::Unregister(fdevent*) {}
diff --git a/adb/fdevent/fdevent_poll.h b/adb/fdevent/fdevent_poll.h
index 98abab2..8803e3e 100644
--- a/adb/fdevent/fdevent_poll.h
+++ b/adb/fdevent/fdevent_poll.h
@@ -48,6 +48,9 @@
     fdevent_context_poll();
     virtual ~fdevent_context_poll();
 
+    virtual void Register(fdevent* fde) final;
+    virtual void Unregister(fdevent* fde) final;
+
     virtual void Set(fdevent* fde, unsigned events) final;
 
     virtual void Loop() final;
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index d17036c..b7b25fa 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -103,12 +103,6 @@
         if (!android::base::ParseNetAddress(addr, &hostname_value, &port_value, serial, error)) {
             return false;
         }
-
-        if (port_value == -1) {
-            *error = "missing port in specification: ";
-            *error += spec;
-            return false;
-        }
     }
 
     if (hostname) {
diff --git a/adb/socket_spec_test.cpp b/adb/socket_spec_test.cpp
index e9d5270..e83c34c 100644
--- a/adb/socket_spec_test.cpp
+++ b/adb/socket_spec_test.cpp
@@ -24,6 +24,13 @@
 #include <android-base/stringprintf.h>
 #include <gtest/gtest.h>
 
+TEST(socket_spec, parse_tcp_socket_spec_failure) {
+    std::string hostname, error, serial;
+    int port;
+    EXPECT_FALSE(parse_tcp_socket_spec("sneakernet:5037", &hostname, &port, &serial, &error));
+    EXPECT_TRUE(error.find("sneakernet") != std::string::npos);
+}
+
 TEST(socket_spec, parse_tcp_socket_spec_just_port) {
     std::string hostname, error, serial;
     int port;
@@ -134,6 +141,19 @@
     EXPECT_NE(client_fd.get(), -1);
 }
 
+TEST(socket_spec, socket_spec_connect_failure) {
+    std::string error, serial;
+    int port;
+    unique_fd client_fd;
+    EXPECT_FALSE(socket_spec_connect(&client_fd, "tcp:", &port, &serial, &error));
+    EXPECT_FALSE(socket_spec_connect(&client_fd, "acceptfd:", &port, &serial, &error));
+    EXPECT_FALSE(socket_spec_connect(&client_fd, "vsock:", &port, &serial, &error));
+    EXPECT_FALSE(socket_spec_connect(&client_fd, "vsock:x", &port, &serial, &error));
+    EXPECT_FALSE(socket_spec_connect(&client_fd, "vsock:5", &port, &serial, &error));
+    EXPECT_FALSE(socket_spec_connect(&client_fd, "vsock:5:x", &port, &serial, &error));
+    EXPECT_FALSE(socket_spec_connect(&client_fd, "sneakernet:", &port, &serial, &error));
+}
+
 TEST(socket_spec, socket_spec_listen_connect_localfilesystem) {
     std::string error, serial;
     int port;
@@ -152,3 +172,16 @@
         EXPECT_NE(client_fd.get(), -1);
     }
 }
+
+TEST(socket_spec, is_socket_spec) {
+    EXPECT_TRUE(is_socket_spec("tcp:blah"));
+    EXPECT_TRUE(is_socket_spec("acceptfd:blah"));
+    EXPECT_TRUE(is_socket_spec("local:blah"));
+    EXPECT_TRUE(is_socket_spec("localreserved:blah"));
+}
+
+TEST(socket_spec, is_local_socket_spec) {
+    EXPECT_TRUE(is_local_socket_spec("local:blah"));
+    EXPECT_TRUE(is_local_socket_spec("tcp:localhost"));
+    EXPECT_FALSE(is_local_socket_spec("tcp:www.google.com"));
+}
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 3a2deb7..bdb786c 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -117,8 +117,10 @@
         "device/main.cpp",
         "device/usb.cpp",
         "device/usb_client.cpp",
+        "device/tcp_client.cpp",
         "device/utility.cpp",
         "device/variables.cpp",
+        "socket.cpp",
     ],
 
     shared_libs: [
@@ -143,12 +145,14 @@
     ],
 
     static_libs: [
+        "libgtest_prod",
         "libhealthhalutils",
         "libsnapshot_nobinder",
         "update_metadata-protos",
     ],
 
     header_libs: [
+        "avb_headers",
         "libsnapshot_headers",
     ]
 }
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index bb085c5..1b0859f 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <android/hardware/boot/1.0/IBootControl.h>
 #include <android/hardware/fastboot/1.0/IFastboot.h>
@@ -28,6 +29,7 @@
 
 #include "constants.h"
 #include "flashing.h"
+#include "tcp_client.h"
 #include "usb_client.h"
 
 using android::fs_mgr::EnsurePathUnmounted;
@@ -60,11 +62,16 @@
               {FB_CMD_GSI, GsiHandler},
               {FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler},
       }),
-      transport_(std::make_unique<ClientUsbTransport>()),
       boot_control_hal_(IBootControl::getService()),
       health_hal_(get_health_service()),
       fastboot_hal_(IFastboot::getService()),
       active_slot_("") {
+    if (android::base::GetProperty("fastbootd.protocol", "usb") == "tcp") {
+        transport_ = std::make_unique<ClientTcpTransport>();
+    } else {
+        transport_ = std::make_unique<ClientUsbTransport>();
+    }
+
     if (boot_control_hal_) {
         boot1_1_ = android::hardware::boot::V1_1::IBootControl::castFrom(boot_control_hal_);
     }
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index fd6ff8e..1bf4c9c 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -31,6 +31,7 @@
 #include <ext4_utils/ext4_utils.h>
 #include <fs_mgr_overlayfs.h>
 #include <fstab/fstab.h>
+#include <libavb/libavb.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
 #include <libsnapshot/snapshot.h>
@@ -122,6 +123,27 @@
     }
 }
 
+static void CopyAVBFooter(std::vector<char>* data, const uint64_t block_device_size) {
+    if (data->size() < AVB_FOOTER_SIZE) {
+        return;
+    }
+    std::string footer;
+    uint64_t footer_offset = data->size() - AVB_FOOTER_SIZE;
+    for (int idx = 0; idx < AVB_FOOTER_MAGIC_LEN; idx++) {
+        footer.push_back(data->at(footer_offset + idx));
+    }
+    if (0 != footer.compare(AVB_FOOTER_MAGIC)) {
+        return;
+    }
+
+    // copy AVB footer from end of data to end of block device
+    uint64_t original_data_size = data->size();
+    data->resize(block_device_size, 0);
+    for (int idx = 0; idx < AVB_FOOTER_SIZE; idx++) {
+        data->at(block_device_size - 1 - idx) = data->at(original_data_size - 1 - idx);
+    }
+}
+
 int Flash(FastbootDevice* device, const std::string& partition_name) {
     PartitionHandle handle;
     if (!OpenPartition(device, partition_name, &handle)) {
@@ -131,8 +153,14 @@
     std::vector<char> data = std::move(device->download_data());
     if (data.size() == 0) {
         return -EINVAL;
-    } else if (data.size() > get_block_device_size(handle.fd())) {
+    }
+    uint64_t block_device_size = get_block_device_size(handle.fd());
+    if (data.size() > block_device_size) {
         return -EOVERFLOW;
+    } else if (data.size() < block_device_size &&
+               (partition_name == "boot" || partition_name == "boot_a" ||
+                partition_name == "boot_b")) {
+        CopyAVBFooter(&data, block_device_size);
     }
     WipeOverlayfsForPartition(device, partition_name);
     int result = FlashBlockDevice(handle.fd(), data);
diff --git a/fastboot/device/tcp_client.cpp b/fastboot/device/tcp_client.cpp
new file mode 100644
index 0000000..ec5e1e3
--- /dev/null
+++ b/fastboot/device/tcp_client.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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 "tcp_client.h"
+#include "constants.h"
+
+#include <android-base/errors.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+static constexpr int kDefaultPort = 5554;
+static constexpr int kProtocolVersion = 1;
+static constexpr int kHandshakeTimeoutMs = 2000;
+static constexpr size_t kHandshakeLength = 4;
+
+// Extract the big-endian 8-byte message length into a 64-bit number.
+static uint64_t ExtractMessageLength(const void* buffer) {
+    uint64_t ret = 0;
+    for (int i = 0; i < 8; ++i) {
+        ret |= uint64_t{reinterpret_cast<const uint8_t*>(buffer)[i]} << (56 - i * 8);
+    }
+    return ret;
+}
+
+// Encode the 64-bit number into a big-endian 8-byte message length.
+static void EncodeMessageLength(uint64_t length, void* buffer) {
+    for (int i = 0; i < 8; ++i) {
+        reinterpret_cast<uint8_t*>(buffer)[i] = length >> (56 - i * 8);
+    }
+}
+
+ClientTcpTransport::ClientTcpTransport() {
+    service_ = Socket::NewServer(Socket::Protocol::kTcp, kDefaultPort);
+
+    // A workaround to notify recovery to continue its work.
+    android::base::SetProperty("sys.usb.ffs.ready", "1");
+}
+
+ssize_t ClientTcpTransport::Read(void* data, size_t len) {
+    if (len > SSIZE_MAX) {
+        return -1;
+    }
+
+    size_t total_read = 0;
+    do {
+        // Read a new message
+        while (message_bytes_left_ == 0) {
+            if (socket_ == nullptr) {
+                ListenFastbootSocket();
+            }
+
+            char buffer[8];
+            if (socket_->ReceiveAll(buffer, 8, 0) == 8) {
+                message_bytes_left_ = ExtractMessageLength(buffer);
+            } else {
+                // If connection is closed by host, Receive will return 0 immediately.
+                socket_.reset(nullptr);
+                // In DATA phase, return error.
+                if (downloading_) {
+                    return -1;
+                }
+            }
+        }
+
+        size_t read_length = len - total_read;
+        if (read_length > message_bytes_left_) {
+            read_length = message_bytes_left_;
+        }
+        ssize_t bytes_read =
+                socket_->ReceiveAll(reinterpret_cast<char*>(data) + total_read, read_length, 0);
+        if (bytes_read == -1) {
+            socket_.reset(nullptr);
+            return -1;
+        } else {
+            message_bytes_left_ -= bytes_read;
+            total_read += bytes_read;
+        }
+    // There are more than one DATA phases if the downloading buffer is too
+    // large, like a very big system image. All of data phases should be
+    // received until the whole buffer is filled in that case.
+    } while (downloading_ && total_read < len);
+
+    return total_read;
+}
+
+ssize_t ClientTcpTransport::Write(const void* data, size_t len) {
+    if (socket_ == nullptr || len > SSIZE_MAX) {
+        return -1;
+    }
+
+    // Use multi-buffer writes for better performance.
+    char header[8];
+    EncodeMessageLength(len, header);
+
+    if (!socket_->Send(std::vector<cutils_socket_buffer_t>{{header, 8}, {data, len}})) {
+        socket_.reset(nullptr);
+        return -1;
+    }
+
+    // In DATA phase
+    if (android::base::StartsWith(reinterpret_cast<const char*>(data), RESPONSE_DATA)) {
+        downloading_ = true;
+    } else {
+        downloading_ = false;
+    }
+
+    return len;
+}
+
+int ClientTcpTransport::Close() {
+    if (socket_ == nullptr) {
+        return -1;
+    }
+    socket_.reset(nullptr);
+
+    return 0;
+}
+
+int ClientTcpTransport::Reset() {
+    return Close();
+}
+
+void ClientTcpTransport::ListenFastbootSocket() {
+    while (true) {
+        socket_ = service_->Accept();
+
+        // Handshake
+        char buffer[kHandshakeLength + 1];
+        buffer[kHandshakeLength] = '\0';
+        if (socket_->ReceiveAll(buffer, kHandshakeLength, kHandshakeTimeoutMs) !=
+            kHandshakeLength) {
+            PLOG(ERROR) << "No Handshake message received";
+            socket_.reset(nullptr);
+            continue;
+        }
+
+        if (memcmp(buffer, "FB", 2) != 0) {
+            PLOG(ERROR) << "Unrecognized initialization message";
+            socket_.reset(nullptr);
+            continue;
+        }
+
+        int version = 0;
+        if (!android::base::ParseInt(buffer + 2, &version) || version < kProtocolVersion) {
+            LOG(ERROR) << "Unknown TCP protocol version " << buffer + 2
+                       << ", our version: " << kProtocolVersion;
+            socket_.reset(nullptr);
+            continue;
+        }
+
+        std::string handshake_message(android::base::StringPrintf("FB%02d", kProtocolVersion));
+        if (!socket_->Send(handshake_message.c_str(), kHandshakeLength)) {
+            PLOG(ERROR) << "Failed to send initialization message";
+            socket_.reset(nullptr);
+            continue;
+        }
+
+        break;
+    }
+}
diff --git a/fastboot/device/tcp_client.h b/fastboot/device/tcp_client.h
new file mode 100644
index 0000000..32e9834
--- /dev/null
+++ b/fastboot/device/tcp_client.h
@@ -0,0 +1,42 @@
+/*
+ * 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 <memory>
+
+#include "socket.h"
+#include "transport.h"
+
+class ClientTcpTransport : public Transport {
+  public:
+    ClientTcpTransport();
+    ~ClientTcpTransport() override = default;
+
+    ssize_t Read(void* data, size_t len) override;
+    ssize_t Write(const void* data, size_t len) override;
+    int Close() override;
+    int Reset() override;
+
+  private:
+    void ListenFastbootSocket();
+
+    std::unique_ptr<Socket> service_;
+    std::unique_ptr<Socket> socket_;
+    uint64_t message_bytes_left_ = 0;
+    bool downloading_ = false;
+
+    DISALLOW_COPY_AND_ASSIGN(ClientTcpTransport);
+};
diff --git a/fastboot/socket.cpp b/fastboot/socket.cpp
index e56ffcf..5a14b63 100644
--- a/fastboot/socket.cpp
+++ b/fastboot/socket.cpp
@@ -54,7 +54,9 @@
     while (total < length) {
         ssize_t bytes = Receive(reinterpret_cast<char*>(data) + total, length - total, timeout_ms);
 
-        if (bytes == -1) {
+        // Returns 0 only when the peer has disconnected because our requested length is not 0. So
+        // we return immediately to avoid dead loop here.
+        if (bytes <= 0) {
             if (total == 0) {
                 return -1;
             }
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 676f446..6cd0430 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -14,6 +14,9 @@
     },
     {
       "name": "vts_libsnapshot_test"
+    },
+    {
+      "name": "libsnapshot_fuzzer_test"
     }
   ]
 }
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index 4505382..2288674 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -124,6 +124,64 @@
     return true;
 }
 
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(
+        const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
+    bool found = false;
+    const uint8_t* desc_partition_name;
+    auto hash_desc = std::make_unique<FsAvbHashDescriptor>();
+
+    for (const auto& vbmeta : vbmeta_images) {
+        size_t num_descriptors;
+        std::unique_ptr<const AvbDescriptor*[], decltype(&avb_free)> descriptors(
+                avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);
+
+        if (!descriptors || num_descriptors < 1) {
+            continue;
+        }
+
+        for (size_t n = 0; n < num_descriptors && !found; n++) {
+            AvbDescriptor desc;
+            if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {
+                LWARNING << "Descriptor[" << n << "] is invalid";
+                continue;
+            }
+            if (desc.tag == AVB_DESCRIPTOR_TAG_HASH) {
+                desc_partition_name = (const uint8_t*)descriptors[n] + sizeof(AvbHashDescriptor);
+                if (!avb_hash_descriptor_validate_and_byteswap((AvbHashDescriptor*)descriptors[n],
+                                                               hash_desc.get())) {
+                    continue;
+                }
+                if (hash_desc->partition_name_len != partition_name.length()) {
+                    continue;
+                }
+                // Notes that desc_partition_name is not NUL-terminated.
+                std::string hash_partition_name((const char*)desc_partition_name,
+                                                hash_desc->partition_name_len);
+                if (hash_partition_name == partition_name) {
+                    found = true;
+                }
+            }
+        }
+
+        if (found) break;
+    }
+
+    if (!found) {
+        LERROR << "Hash descriptor not found: " << partition_name;
+        return nullptr;
+    }
+
+    hash_desc->partition_name = partition_name;
+
+    const uint8_t* desc_salt = desc_partition_name + hash_desc->partition_name_len;
+    hash_desc->salt = BytesToHex(desc_salt, hash_desc->salt_len);
+
+    const uint8_t* desc_digest = desc_salt + hash_desc->salt_len;
+    hash_desc->digest = BytesToHex(desc_digest, hash_desc->digest_len);
+
+    return hash_desc;
+}
+
 std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
         const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
     bool found = false;
diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h
index 09c786a..e8f7c39 100644
--- a/fs_mgr/libfs_avb/avb_util.h
+++ b/fs_mgr/libfs_avb/avb_util.h
@@ -40,6 +40,9 @@
 std::string GetAvbPropertyDescriptor(const std::string& key,
                                      const std::vector<VBMetaData>& vbmeta_images);
 
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(
+        const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
+
 // AvbHashtreeDescriptor to dm-verity table setup.
 std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
         const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
diff --git a/fs_mgr/libfs_avb/fs_avb_util.cpp b/fs_mgr/libfs_avb/fs_avb_util.cpp
index f82f83d..1c14cc0 100644
--- a/fs_mgr/libfs_avb/fs_avb_util.cpp
+++ b/fs_mgr/libfs_avb/fs_avb_util.cpp
@@ -74,5 +74,15 @@
     return GetHashtreeDescriptor(avb_partition_name, vbmeta_images);
 }
 
+// Given a path, loads and verifies the vbmeta, to extract the Avb Hash descriptor.
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(const std::string& avb_partition_name,
+                                                       VBMetaData&& vbmeta) {
+    if (!vbmeta.size()) return nullptr;
+
+    std::vector<VBMetaData> vbmeta_images;
+    vbmeta_images.emplace_back(std::move(vbmeta));
+    return GetHashDescriptor(avb_partition_name, vbmeta_images);
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
index ec8badb..3f37bd7 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
@@ -32,9 +32,20 @@
                                                 std::string* out_avb_partition_name,
                                                 VBMetaVerifyResult* out_verify_result);
 
+// Loads the single vbmeta from a given path.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmetaByPath(
+        const std::string& image_path, const std::string& partition_name,
+        const std::string& expected_public_key_blob, bool allow_verification_error,
+        bool rollback_protection, bool is_chained_vbmeta, std::string* out_public_key_data,
+        bool* out_verification_disabled, VBMetaVerifyResult* out_verify_result);
+
 // Gets the hashtree descriptor for avb_partition_name from the vbmeta.
 std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
         const std::string& avb_partition_name, VBMetaData&& vbmeta);
 
+// Gets the hash descriptor for avb_partition_name from the vbmeta.
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(const std::string& avb_partition_name,
+                                                       VBMetaData&& vbmeta);
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/types.h b/fs_mgr/libfs_avb/include/fs_avb/types.h
index bd638e6..f2aa7cc 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/types.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/types.h
@@ -55,6 +55,12 @@
 
 std::ostream& operator<<(std::ostream& os, AvbHandleStatus status);
 
+struct FsAvbHashDescriptor : AvbHashDescriptor {
+    std::string partition_name;
+    std::string salt;
+    std::string digest;
+};
+
 struct FsAvbHashtreeDescriptor : AvbHashtreeDescriptor {
     std::string partition_name;
     std::string salt;
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index e916693..2783e4d 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -246,8 +246,8 @@
     gtest: false,
 }
 
-cc_fuzz {
-    name: "libsnapshot_fuzzer",
+cc_defaults {
+    name: "libsnapshot_fuzzer_defaults",
 
     // TODO(b/154633114): make host supported.
     // host_supported: true,
@@ -289,7 +289,12 @@
         canonical_path_from_root: false,
         local_include_dirs: ["."],
     },
+}
 
+cc_fuzz {
+    name: "libsnapshot_fuzzer",
+    defaults: ["libsnapshot_fuzzer_defaults"],
+    corpus: ["corpus/*"],
     fuzz_config: {
         cc: ["android-virtual-ab+bugs@google.com"],
         componentid: 30545,
@@ -298,3 +303,14 @@
         fuzz_on_haiku_device: true,
     },
 }
+
+cc_test {
+    name: "libsnapshot_fuzzer_test",
+    defaults: ["libsnapshot_fuzzer_defaults"],
+    data: ["corpus/*"],
+    test_suites: [
+        "device-tests",
+    ],
+    auto_gen_config: true,
+    require_root: true,
+}
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
index 91fbb60..a55b42a 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
@@ -64,6 +64,7 @@
         bool has_metadata_device_object = 1;
         bool metadata_mounted = 2;
     }
+    reserved 18 to 9999;
     oneof value {
         NoArgs begin_update = 1;
         NoArgs cancel_update = 2;
@@ -82,6 +83,9 @@
         NoArgs dump = 15;
         NoArgs ensure_metadata_mounted = 16;
         NoArgs get_snapshot_merge_stats_instance = 17;
+
+        // Test directives that has nothing to do with ISnapshotManager API surface.
+        NoArgs switch_slot = 10000;
     }
 }
 
@@ -97,7 +101,10 @@
     bool is_super_metadata_valid = 3;
     chromeos_update_engine.DeltaArchiveManifest super_data = 4;
 
+    // Whether the directory that mocks /metadata/ota/snapshot is created.
+    bool has_metadata_snapshots_dir = 5;
+
     // More data used to prep the test before running actions.
-    reserved 5 to 9999;
+    reserved 6 to 9999;
     repeated SnapshotManagerActionProto actions = 10000;
 }
diff --git a/fs_mgr/libsnapshot/corpus/launch_device.txt b/fs_mgr/libsnapshot/corpus/launch_device.txt
new file mode 100644
index 0000000..55a7f2c
--- /dev/null
+++ b/fs_mgr/libsnapshot/corpus/launch_device.txt
@@ -0,0 +1,161 @@
+device_info_data {
+  slot_suffix_is_a: true
+  is_overlayfs_setup: false
+  allow_set_boot_control_merge_status: true
+  allow_set_slot_as_unbootable: true
+  is_recovery: false
+}
+manager_data {
+  is_local_image_manager: false
+}
+is_super_metadata_valid: true
+super_data {
+  partitions {
+    partition_name: "sys_a"
+    new_partition_info {
+      size: 3145728
+    }
+  }
+  partitions {
+    partition_name: "vnd_a"
+    new_partition_info {
+      size: 3145728
+    }
+  }
+  partitions {
+    partition_name: "prd_a"
+    new_partition_info {
+      size: 3145728
+    }
+  }
+  dynamic_partition_metadata {
+    groups {
+      name: "group_google_dp_a"
+      size: 15728640
+      partition_names: "sys_a"
+      partition_names: "vnd_a"
+      partition_names: "prd_a"
+    }
+  }
+}
+has_metadata_snapshots_dir: true
+actions {
+  begin_update {
+  }
+}
+actions {
+  create_update_snapshots {
+    partitions {
+      partition_name: "sys"
+      new_partition_info {
+        size: 3878912
+      }
+      operations {
+        type: ZERO,
+        dst_extents {
+          start_block: 0
+          num_blocks: 947
+        }
+      }
+    }
+    partitions {
+      partition_name: "vnd"
+      new_partition_info {
+        size: 3878912
+      }
+      operations {
+        type: ZERO,
+        dst_extents {
+          start_block: 0
+          num_blocks: 947
+        }
+      }
+    }
+    partitions {
+      partition_name: "prd"
+      new_partition_info {
+        size: 3878912
+      }
+      operations {
+        type: ZERO,
+        dst_extents {
+          start_block: 0
+          num_blocks: 947
+        }
+      }
+    }
+    dynamic_partition_metadata {
+      groups {
+        name: "group_google_dp"
+        size: 15728640
+        partition_names: "sys"
+        partition_names: "vnd"
+        partition_names: "prd"
+      }
+    }
+  }
+}
+actions {
+  map_update_snapshot {
+    use_correct_super: true
+    has_metadata_slot: true
+    metadata_slot: 1
+    partition_name: "sys_b"
+    force_writable: true
+    timeout_millis: 3000
+  }
+}
+actions {
+  map_update_snapshot {
+    use_correct_super: true
+    has_metadata_slot: true
+    metadata_slot: 1
+    partition_name: "vnd_b"
+    force_writable: true
+    timeout_millis: 3000
+  }
+}
+actions {
+  map_update_snapshot {
+    use_correct_super: true
+    has_metadata_slot: true
+    metadata_slot: 1
+    partition_name: "prd_b"
+    force_writable: true
+    timeout_millis: 3000
+  }
+}
+actions {
+  finished_snapshot_writes: false
+}
+actions {
+  unmap_update_snapshot: "sys_b"
+}
+actions {
+  unmap_update_snapshot: "vnd_b"
+}
+actions {
+  unmap_update_snapshot: "prd_b"
+}
+actions {
+  switch_slot {
+  }
+}
+actions {
+  need_snapshots_in_first_stage_mount {
+  }
+}
+actions {
+  create_logical_and_snapshot_partitions {
+    use_correct_super: true
+    timeout_millis: 5000
+  }
+}
+actions {
+  initiate_merge {
+  }
+}
+actions {
+  process_update_state {
+  }
+}
diff --git a/fs_mgr/libsnapshot/fuzz.sh b/fs_mgr/libsnapshot/fuzz.sh
index 2910129..0e57674 100755
--- a/fs_mgr/libsnapshot/fuzz.sh
+++ b/fs_mgr/libsnapshot/fuzz.sh
@@ -3,7 +3,8 @@
 FUZZ_TARGET=libsnapshot_fuzzer
 TARGET_ARCH=$(get_build_var TARGET_ARCH)
 FUZZ_BINARY=/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/${FUZZ_TARGET}
-DEVICE_CORPSE_DIR=/data/local/tmp/${FUZZ_TARGET}
+DEVICE_INIT_CORPUS_DIR=/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/corpus
+DEVICE_GENERATED_CORPUS_DIR=/data/local/tmp/${FUZZ_TARGET}/corpus
 DEVICE_GCOV_DIR=/data/local/tmp/${FUZZ_TARGET}/gcov
 HOST_SCRATCH_DIR=/tmp/${FUZZ_TARGET}
 GCOV_TOOL=${HOST_SCRATCH_DIR}/llvm-gcov
@@ -26,13 +27,14 @@
 
 prepare_device() {
     adb root && adb remount &&
-    adb shell mkdir -p ${DEVICE_CORPSE_DIR} &&
+    adb shell mkdir -p ${DEVICE_GENERATED_CORPUS_DIR} &&
     adb shell rm -rf ${DEVICE_GCOV_DIR} &&
     adb shell mkdir -p ${DEVICE_GCOV_DIR}
 }
 
 push_binary() {
-    adb push ${ANDROID_PRODUCT_OUT}/${FUZZ_BINARY} ${FUZZ_BINARY}
+    adb push ${ANDROID_PRODUCT_OUT}/${FUZZ_BINARY} ${FUZZ_BINARY} &&
+    adb push ${ANDROID_PRODUCT_OUT}/${DEVICE_INIT_CORPUS_DIR} $(dirname ${FUZZ_BINARY})
 }
 
 prepare_host() {
@@ -52,7 +54,7 @@
     prepare_device &&
     build_normal &&
     push_binary &&
-    adb shell ${FUZZ_BINARY} "$@" ${DEVICE_CORPSE_DIR}
+    adb shell ${FUZZ_BINARY} "$@" ${DEVICE_INIT_CORPUS_DIR} ${DEVICE_GENERATED_CORPUS_DIR}
 }
 
 run_snapshot_fuzz() {
@@ -62,7 +64,7 @@
     adb shell GCOV_PREFIX=${DEVICE_GCOV_DIR} GCOV_PREFIX_STRIP=3 \
         ${FUZZ_BINARY} \
         -runs=0 \
-        ${DEVICE_CORPSE_DIR}
+        ${DEVICE_INIT_CORPUS_DIR} ${DEVICE_GENERATED_CORPUS_DIR}
 }
 
 show_fuzz_result() {
@@ -82,7 +84,7 @@
 
 # run_snapshot_fuzz -runs=10000
 run_snapshot_fuzz_all() {
-    generate_corpse "$@" &&
+    generate_corpus "$@" &&
     run_snapshot_fuzz &&
     show_fuzz_result
 }
diff --git a/fs_mgr/libsnapshot/fuzz_utils.h b/fs_mgr/libsnapshot/fuzz_utils.h
index 4dc6cdc..20b13b2 100644
--- a/fs_mgr/libsnapshot/fuzz_utils.h
+++ b/fs_mgr/libsnapshot/fuzz_utils.h
@@ -68,17 +68,25 @@
     return 0;
 }
 
+// Get the field descriptor for the oneof field in the action message. If no oneof field is set,
+// return nullptr.
 template <typename Action>
-void ExecuteActionProto(typename Action::Class* module,
-                        const typename Action::Proto& action_proto) {
+const google::protobuf::FieldDescriptor* GetValueFieldDescriptor(
+        const typename Action::Proto& action_proto) {
     static auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor());
 
     auto* action_refl = Action::Proto::GetReflection();
     if (!action_refl->HasOneof(action_proto, action_value_desc)) {
-        return;
+        return nullptr;
     }
+    return action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc);
+}
 
-    const auto* field_desc = action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc);
+template <typename Action>
+void ExecuteActionProto(typename Action::ClassType* module,
+                        const typename Action::Proto& action_proto) {
+    const auto* field_desc = GetValueFieldDescriptor<Action>(action_proto);
+    if (field_desc == nullptr) return;
     auto number = field_desc->number();
     const auto& map = *Action::GetFunctionMap();
     auto it = map.find(number);
@@ -89,7 +97,7 @@
 
 template <typename Action>
 void ExecuteAllActionProtos(
-        typename Action::Class* module,
+        typename Action::ClassType* module,
         const google::protobuf::RepeatedPtrField<typename Action::Proto>& action_protos) {
     for (const auto& proto : action_protos) {
         ExecuteActionProto<Action>(module, proto);
@@ -134,53 +142,57 @@
 // ActionPerformer extracts arguments from the protobuf message, and then call FuzzFunction
 // with these arguments.
 template <typename FuzzFunction, typename Signature, typename Enabled = void>
-struct ActionPerfomer;  // undefined
+struct ActionPerformerImpl;  // undefined
 
 template <typename FuzzFunction, typename MessageProto>
-struct ActionPerfomer<
+struct ActionPerformerImpl<
         FuzzFunction, void(const MessageProto&),
         typename std::enable_if_t<std::is_base_of_v<google::protobuf::Message, MessageProto>>> {
-    static void Invoke(typename FuzzFunction::Class* module,
-                       const google::protobuf::Message& action_proto,
-                       const google::protobuf::FieldDescriptor* field_desc) {
+    static typename FuzzFunction::ReturnType Invoke(
+            typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
+            const google::protobuf::FieldDescriptor* field_desc) {
         const MessageProto& arg = CheckedCast<std::remove_reference_t<MessageProto>>(
                 action_proto.GetReflection()->GetMessage(action_proto, field_desc));
-        FuzzFunction::ImplBody(module, arg);
+        return FuzzFunction::ImplBody(module, arg);
     }
 };
 
 template <typename FuzzFunction, typename Primitive>
-struct ActionPerfomer<FuzzFunction, void(Primitive),
-                      typename std::enable_if_t<std::is_arithmetic_v<Primitive>>> {
-    static void Invoke(typename FuzzFunction::Class* module,
-                       const google::protobuf::Message& action_proto,
-                       const google::protobuf::FieldDescriptor* field_desc) {
+struct ActionPerformerImpl<FuzzFunction, void(Primitive),
+                           typename std::enable_if_t<std::is_arithmetic_v<Primitive>>> {
+    static typename FuzzFunction::ReturnType Invoke(
+            typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
+            const google::protobuf::FieldDescriptor* field_desc) {
         Primitive arg = std::invoke(PrimitiveGetter<Primitive>::fp, action_proto.GetReflection(),
                                     action_proto, field_desc);
-        FuzzFunction::ImplBody(module, arg);
+        return FuzzFunction::ImplBody(module, arg);
     }
 };
 
 template <typename FuzzFunction>
-struct ActionPerfomer<FuzzFunction, void()> {
-    static void Invoke(typename FuzzFunction::Class* module, const google::protobuf::Message&,
-                       const google::protobuf::FieldDescriptor*) {
-        FuzzFunction::ImplBody(module);
+struct ActionPerformerImpl<FuzzFunction, void()> {
+    static typename FuzzFunction::ReturnType Invoke(typename FuzzFunction::ClassType* module,
+                                                    const google::protobuf::Message&,
+                                                    const google::protobuf::FieldDescriptor*) {
+        return FuzzFunction::ImplBody(module);
     }
 };
 
 template <typename FuzzFunction>
-struct ActionPerfomer<FuzzFunction, void(const std::string&)> {
-    static void Invoke(typename FuzzFunction::Class* module,
-                       const google::protobuf::Message& action_proto,
-                       const google::protobuf::FieldDescriptor* field_desc) {
+struct ActionPerformerImpl<FuzzFunction, void(const std::string&)> {
+    static typename FuzzFunction::ReturnType Invoke(
+            typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
+            const google::protobuf::FieldDescriptor* field_desc) {
         std::string scratch;
         const std::string& arg = action_proto.GetReflection()->GetStringReference(
                 action_proto, field_desc, &scratch);
-        FuzzFunction::ImplBody(module, arg);
+        return FuzzFunction::ImplBody(module, arg);
     }
 };
 
+template <typename FuzzFunction>
+struct ActionPerformer : ActionPerformerImpl<FuzzFunction, typename FuzzFunction::Signature> {};
+
 }  // namespace android::fuzz
 
 // Fuzz existing C++ class, ClassType, with a collection of functions under the name Action.
@@ -197,11 +209,11 @@
 //   FUZZ_CLASS(Foo, FooAction)
 // After linking functions of Foo to FooAction, execute all actions by:
 //   FooAction::ExecuteAll(foo_object, action_protos)
-#define FUZZ_CLASS(ClassType, Action)                                                            \
+#define FUZZ_CLASS(Class, Action)                                                                \
     class Action {                                                                               \
       public:                                                                                    \
         using Proto = Action##Proto;                                                             \
-        using Class = ClassType;                                                                 \
+        using ClassType = Class;                                                                 \
         using FunctionMap = android::fuzz::FunctionMap<Class>;                                   \
         static FunctionMap* GetFunctionMap() {                                                   \
             static Action::FunctionMap map;                                                      \
@@ -225,29 +237,33 @@
 // }
 // class Foo { public: void DoAwesomeFoo(bool arg); };
 // FUZZ_OBJECT(FooAction, Foo);
-// FUZZ_FUNCTION(FooAction, DoFoo, module, bool arg) {
+// FUZZ_FUNCTION(FooAction, DoFoo, void, IFoo* module, bool arg) {
 //   module->DoAwesomeFoo(arg);
 // }
 // The name DoFoo is the camel case name of the action in protobuf definition of FooActionProto.
-#define FUZZ_FUNCTION(Action, FunctionName, module, ...)                                         \
-    class FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) {                                       \
-      public:                                                                                    \
-        using Class = Action::Class;                                                             \
-        static void ImplBody(Action::Class*, ##__VA_ARGS__);                                     \
-                                                                                                 \
-      private:                                                                                   \
-        static bool registered_;                                                                 \
-    };                                                                                           \
-    auto FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::registered_ = ([] {                     \
-        auto tag = Action::Proto::ValueCase::FUZZ_FUNCTION_TAG_NAME(FunctionName);               \
-        auto func =                                                                              \
-                &::android::fuzz::ActionPerfomer<FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName), \
-                                                 void(__VA_ARGS__)>::Invoke;                     \
-        Action::GetFunctionMap()->CheckEmplace(tag, func);                                       \
-        return true;                                                                             \
-    })();                                                                                        \
-    void FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::ImplBody(Action::Class* module,         \
-                                                                  ##__VA_ARGS__)
+#define FUZZ_FUNCTION(Action, FunctionName, Return, ModuleArg, ...)             \
+    class FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) {                      \
+      public:                                                                   \
+        using ActionType = Action;                                              \
+        using ClassType = Action::ClassType;                                    \
+        using ReturnType = Return;                                              \
+        using Signature = void(__VA_ARGS__);                                    \
+        static constexpr const char name[] = #FunctionName;                     \
+        static constexpr const auto tag =                                       \
+                Action::Proto::ValueCase::FUZZ_FUNCTION_TAG_NAME(FunctionName); \
+        static ReturnType ImplBody(ModuleArg, ##__VA_ARGS__);                   \
+                                                                                \
+      private:                                                                  \
+        static bool registered_;                                                \
+    };                                                                          \
+    auto FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::registered_ = ([] {    \
+        auto tag = FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::tag;         \
+        auto func = &::android::fuzz::ActionPerformer<FUZZ_FUNCTION_CLASS_NAME( \
+                Action, FunctionName)>::Invoke;                                 \
+        Action::GetFunctionMap()->CheckEmplace(tag, func);                      \
+        return true;                                                            \
+    })();                                                                       \
+    Return FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::ImplBody(ModuleArg, ##__VA_ARGS__)
 
 // Implement a simple action by linking it to the function with the same name. Example:
 // message FooActionProto {
@@ -261,5 +277,9 @@
 // FUZZ_FUNCTION(FooAction, DoBar);
 // The name DoBar is the camel case name of the action in protobuf definition of FooActionProto, and
 // also the name of the function of Foo.
-#define FUZZ_SIMPLE_FUNCTION(Action, FunctionName) \
-    FUZZ_FUNCTION(Action, FunctionName, module) { (void)module->FunctionName(); }
+#define FUZZ_SIMPLE_FUNCTION(Action, FunctionName)                            \
+    FUZZ_FUNCTION(Action, FunctionName,                                       \
+                  decltype(std::declval<Action::ClassType>().FunctionName()), \
+                  Action::ClassType* module) {                                \
+        return module->FunctionName();                                        \
+    }
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
index 421154d..5b145c3 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
@@ -21,14 +21,21 @@
 #include <tuple>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/result.h>
+#include <gtest/gtest.h>
 #include <src/libfuzzer/libfuzzer_macro.h>
 #include <storage_literals/storage_literals.h>
 
 #include "fuzz_utils.h"
 #include "snapshot_fuzz_utils.h"
 
+using android::base::Error;
+using android::base::GetBoolProperty;
 using android::base::LogId;
 using android::base::LogSeverity;
+using android::base::ReadFileToString;
+using android::base::Result;
 using android::base::SetLogger;
 using android::base::StderrLogger;
 using android::base::StdioLogger;
@@ -37,6 +44,8 @@
 using android::snapshot::SnapshotFuzzData;
 using android::snapshot::SnapshotFuzzEnv;
 using chromeos_update_engine::DeltaArchiveManifest;
+using google::protobuf::FieldDescriptor;
+using google::protobuf::Message;
 using google::protobuf::RepeatedPtrField;
 
 // Avoid linking to libgsi since it needs disk I/O.
@@ -54,6 +63,7 @@
 namespace android::snapshot {
 
 const SnapshotFuzzData* current_data = nullptr;
+const SnapshotTestModule* current_module = nullptr;
 
 SnapshotFuzzEnv* GetSnapshotFuzzEnv();
 
@@ -73,48 +83,49 @@
 FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, EnsureMetadataMounted);
 FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, GetSnapshotMergeStatsInstance);
 
-#define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ...) \
-    FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, snapshot, ##__VA_ARGS__)
+#define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ReturnType, ...)                                  \
+    FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, ReturnType, ISnapshotManager* snapshot, \
+                  ##__VA_ARGS__)
 
-SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool wipe) {
-    (void)snapshot->FinishedSnapshotWrites(wipe);
+SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool, bool wipe) {
+    return snapshot->FinishedSnapshotWrites(wipe);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, const ProcessUpdateStateArgs& args) {
+SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, bool, const ProcessUpdateStateArgs& args) {
     std::function<bool()> before_cancel;
     if (args.has_before_cancel()) {
         before_cancel = [&]() { return args.fail_before_cancel(); };
     }
-    (void)snapshot->ProcessUpdateState({}, before_cancel);
+    return snapshot->ProcessUpdateState({}, before_cancel);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, bool has_progress_arg) {
+SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, UpdateState, bool has_progress_arg) {
     double progress;
-    (void)snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr);
+    return snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool has_callback) {
+SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool, bool has_callback) {
     std::function<void()> callback;
     if (has_callback) {
         callback = []() {};
     }
-    (void)snapshot->HandleImminentDataWipe(callback);
+    return snapshot->HandleImminentDataWipe(callback);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(Dump) {
+SNAPSHOT_FUZZ_FUNCTION(Dump, bool) {
     std::stringstream ss;
-    (void)snapshot->Dump(ss);
+    return snapshot->Dump(ss);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, const DeltaArchiveManifest& manifest) {
-    (void)snapshot->CreateUpdateSnapshots(manifest);
+SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, bool, const DeltaArchiveManifest& manifest) {
+    return snapshot->CreateUpdateSnapshots(manifest);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, const std::string& name) {
-    (void)snapshot->UnmapUpdateSnapshot(name);
+SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, bool, const std::string& name) {
+    return snapshot->UnmapUpdateSnapshot(name);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions,
+SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions, bool,
                        const CreateLogicalAndSnapshotPartitionsArgs& args) {
     const std::string* super;
     if (args.use_correct_super()) {
@@ -122,20 +133,21 @@
     } else {
         super = &args.super();
     }
-    (void)snapshot->CreateLogicalAndSnapshotPartitions(
+    return snapshot->CreateLogicalAndSnapshotPartitions(
             *super, std::chrono::milliseconds(args.timeout_millis()));
 }
 
-SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata,
+SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata, CreateResult,
                        const RecoveryCreateSnapshotDevicesArgs& args) {
     std::unique_ptr<AutoDevice> device;
     if (args.has_metadata_device_object()) {
         device = std::make_unique<DummyAutoDevice>(args.metadata_mounted());
     }
-    (void)snapshot->RecoveryCreateSnapshotDevices(device);
+    return snapshot->RecoveryCreateSnapshotDevices(device);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, const CreateLogicalPartitionParamsProto& params_proto) {
+SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, bool,
+                       const CreateLogicalPartitionParamsProto& params_proto) {
     auto partition_opener = std::make_unique<TestPartitionOpener>(GetSnapshotFuzzEnv()->super());
     CreateLogicalPartitionParams params;
     if (params_proto.use_correct_super()) {
@@ -152,7 +164,14 @@
     params.device_name = params_proto.device_name();
     params.partition_opener = partition_opener.get();
     std::string path;
-    (void)snapshot->MapUpdateSnapshot(params, &path);
+    return snapshot->MapUpdateSnapshot(params, &path);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(SwitchSlot, void) {
+    (void)snapshot;
+    CHECK(current_module != nullptr);
+    CHECK(current_module->device_info != nullptr);
+    current_module->device_info->SwitchSlot();
 }
 
 // During global init, log all messages to stdio. This is only done once.
@@ -186,7 +205,8 @@
 }
 // Stop logging (except fatal messages) after global initialization. This is only done once.
 int StopLoggingAfterGlobalInit() {
-    [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silincer;
+    (void)GetSnapshotFuzzEnv();
+    [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silencer;
     SetLogger(&FatalOnlyLogger);
     return 0;
 }
@@ -194,22 +214,139 @@
 SnapshotFuzzEnv* GetSnapshotFuzzEnv() {
     [[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit();
     static SnapshotFuzzEnv env;
-    [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
     return &env;
 }
 
+SnapshotTestModule SetUpTest(const SnapshotFuzzData& snapshot_fuzz_data) {
+    current_data = &snapshot_fuzz_data;
+
+    auto env = GetSnapshotFuzzEnv();
+    env->CheckSoftReset();
+
+    auto test_module = env->CheckCreateSnapshotManager(snapshot_fuzz_data);
+    current_module = &test_module;
+    CHECK(test_module.snapshot);
+    return test_module;
+}
+
+void TearDownTest() {
+    current_module = nullptr;
+    current_data = nullptr;
+}
+
 }  // namespace android::snapshot
 
 DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) {
     using namespace android::snapshot;
 
-    current_data = &snapshot_fuzz_data;
-
-    auto env = GetSnapshotFuzzEnv();
-    env->CheckSoftReset();
-
-    auto snapshot_manager = env->CheckCreateSnapshotManager(snapshot_fuzz_data);
-    CHECK(snapshot_manager);
-
-    SnapshotManagerAction::ExecuteAll(snapshot_manager.get(), snapshot_fuzz_data.actions());
+    [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
+    auto test_module = SetUpTest(snapshot_fuzz_data);
+    SnapshotManagerAction::ExecuteAll(test_module.snapshot.get(), snapshot_fuzz_data.actions());
+    TearDownTest();
 }
+
+namespace android::snapshot {
+
+// Work-around to cast a 'void' value to Result<void>.
+template <typename T>
+struct GoodResult {
+    template <typename F>
+    static Result<T> Cast(F&& f) {
+        return f();
+    }
+};
+
+template <>
+struct GoodResult<void> {
+    template <typename F>
+    static Result<void> Cast(F&& f) {
+        f();
+        return {};
+    }
+};
+
+class LibsnapshotFuzzerTest : public ::testing::Test {
+  protected:
+    static void SetUpTestCase() {
+        // Do initialization once.
+        (void)GetSnapshotFuzzEnv();
+    }
+    void SetUp() override {
+        bool is_virtual_ab = GetBoolProperty("ro.virtual_ab.enabled", false);
+        if (!is_virtual_ab) GTEST_SKIP() << "Test only runs on Virtual A/B devices.";
+    }
+    void SetUpFuzzData(const std::string& fn) {
+        auto path = android::base::GetExecutableDirectory() + "/corpus/"s + fn;
+        std::string proto_text;
+        ASSERT_TRUE(ReadFileToString(path, &proto_text));
+        snapshot_fuzz_data_ = std::make_unique<SnapshotFuzzData>();
+        ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(proto_text,
+                                                                  snapshot_fuzz_data_.get()));
+        test_module_ = android::snapshot::SetUpTest(*snapshot_fuzz_data_);
+    }
+    void TearDown() override { android::snapshot::TearDownTest(); }
+    template <typename FuzzFunction>
+    Result<typename FuzzFunction::ReturnType> Execute(int action_index) {
+        if (action_index >= snapshot_fuzz_data_->actions_size()) {
+            return Error() << "Index " << action_index << " is out of bounds ("
+                           << snapshot_fuzz_data_->actions_size() << " actions in corpus";
+        }
+        const auto& action_proto = snapshot_fuzz_data_->actions(action_index);
+        const auto* field_desc =
+                android::fuzz::GetValueFieldDescriptor<typename FuzzFunction::ActionType>(
+                        action_proto);
+        if (field_desc == nullptr) {
+            return Error() << "Action at index " << action_index << " has no value defined.";
+        }
+        if (FuzzFunction::tag != field_desc->number()) {
+            return Error() << "Action at index " << action_index << " is expected to be "
+                           << FuzzFunction::name << ", but it is " << field_desc->name()
+                           << " in corpus.";
+        }
+        return GoodResult<typename FuzzFunction::ReturnType>::Cast([&]() {
+            return android::fuzz::ActionPerformer<FuzzFunction>::Invoke(test_module_.snapshot.get(),
+                                                                        action_proto, field_desc);
+        });
+    }
+
+    std::unique_ptr<SnapshotFuzzData> snapshot_fuzz_data_;
+    SnapshotTestModule test_module_;
+};
+
+#define SNAPSHOT_FUZZ_FN_NAME(name) FUZZ_FUNCTION_CLASS_NAME(SnapshotManagerAction, name)
+
+MATCHER_P(ResultIs, expected, "") {
+    if (!arg.ok()) {
+        *result_listener << arg.error();
+        return false;
+    }
+    *result_listener << "expected: " << expected;
+    return arg.value() == expected;
+}
+
+#define ASSERT_RESULT_TRUE(actual) ASSERT_THAT(actual, ResultIs(true))
+
+// Check that launch_device.txt is executed correctly.
+TEST_F(LibsnapshotFuzzerTest, LaunchDevice) {
+    SetUpFuzzData("launch_device.txt");
+
+    int i = 0;
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(BeginUpdate)>(i++));
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(CreateUpdateSnapshots)>(i++));
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "sys_b";
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "vnd_b";
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "prd_b";
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(FinishedSnapshotWrites)>(i++));
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "sys_b";
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "vnd_b";
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "prd_b";
+    ASSERT_RESULT_OK(Execute<SNAPSHOT_FUZZ_FN_NAME(SwitchSlot)>(i++));
+    ASSERT_EQ("_b", test_module_.device_info->GetSlotSuffix());
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(NeedSnapshotsInFirstStageMount)>(i++));
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(CreateLogicalAndSnapshotPartitions)>(i++));
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(InitiateMerge)>(i++));
+    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(ProcessUpdateState)>(i++));
+    ASSERT_EQ(i, snapshot_fuzz_data_->actions_size()) << "Not all actions are executed.";
+}
+
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
index 8101d03..8926535 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
@@ -24,7 +24,10 @@
 
 #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 <fs_mgr.h>
 #include <libsnapshot/auto_device.h>
 #include <libsnapshot/snapshot.h>
 #include <storage_literals/storage_literals.h>
@@ -41,21 +44,30 @@
 using namespace std::chrono_literals;
 using namespace std::string_literals;
 
+using android::base::Basename;
+using android::base::ReadFileToString;
+using android::base::SetProperty;
+using android::base::Split;
+using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::unique_fd;
 using android::base::WriteStringToFile;
+using android::dm::DeviceMapper;
+using android::dm::DmTarget;
 using android::dm::LoopControl;
 using android::fiemap::IImageManager;
 using android::fiemap::ImageManager;
 using android::fs_mgr::BlockDeviceInfo;
+using android::fs_mgr::FstabEntry;
 using android::fs_mgr::IPartitionOpener;
 using chromeos_update_engine::DynamicPartitionMetadata;
 
-// This directory is exempted from pinning in ImageManager.
-static const char MNT_DIR[] = "/data/gsi/ota/test/";
+static const char MNT_DIR[] = "/mnt";
+static const char BLOCK_SYSFS[] = "/sys/block";
 
 static const char FAKE_ROOT_NAME[] = "snapshot_fuzz";
 static const auto SUPER_IMAGE_SIZE = 16_MiB;
+static const auto DATA_IMAGE_SIZE = 16_MiB;
 static const auto FAKE_ROOT_SIZE = 64_MiB;
 
 namespace android::snapshot {
@@ -98,6 +110,141 @@
     return nftw(path.c_str(), callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) == 0;
 }
 
+std::string GetLinearBaseDeviceString(const DeviceMapper::TargetInfo& target) {
+    if (target.spec.target_type != "linear"s) return {};
+    auto tokens = Split(target.data, " ");
+    CHECK_EQ(2, tokens.size());
+    return tokens[0];
+}
+
+std::vector<std::string> GetSnapshotBaseDeviceStrings(const DeviceMapper::TargetInfo& target) {
+    if (target.spec.target_type != "snapshot"s && target.spec.target_type != "snapshot-merge"s)
+        return {};
+    auto tokens = Split(target.data, " ");
+    CHECK_EQ(4, tokens.size());
+    return {tokens[0], tokens[1]};
+}
+
+bool ShouldDeleteLoopDevice(const std::string& node) {
+    std::string backing_file;
+    if (ReadFileToString(StringPrintf("%s/loop/backing_file", node.data()), &backing_file)) {
+        if (StartsWith(backing_file, std::string(MNT_DIR) + "/" + FAKE_ROOT_NAME)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+std::vector<DeviceMapper::TargetInfo> GetTableInfoIfExists(const std::string& dev_name) {
+    auto& dm = DeviceMapper::Instance();
+    std::vector<DeviceMapper::TargetInfo> table;
+    if (!dm.GetTableInfo(dev_name, &table)) {
+        PCHECK(errno == ENODEV);
+        return {};
+    }
+    return table;
+}
+
+std::set<std::string> GetAllBaseDeviceStrings(const std::string& child_dev) {
+    std::set<std::string> ret;
+    for (const auto& child_target : GetTableInfoIfExists(child_dev)) {
+        auto snapshot_bases = GetSnapshotBaseDeviceStrings(child_target);
+        ret.insert(snapshot_bases.begin(), snapshot_bases.end());
+
+        auto linear_base = GetLinearBaseDeviceString(child_target);
+        if (!linear_base.empty()) {
+            ret.insert(linear_base);
+        }
+    }
+    return ret;
+}
+
+using PropertyList = std::set<std::string>;
+void InsertProperty(const char* key, const char* /*name*/, void* cookie) {
+    reinterpret_cast<PropertyList*>(cookie)->insert(key);
+}
+
+// Attempt to delete all devices that is based on dev_name, including itself.
+void CheckDeleteDeviceMapperTree(const std::string& dev_name, bool known_allow_delete = false,
+                                 uint64_t depth = 100) {
+    CHECK(depth > 0) << "Reaching max depth when deleting " << dev_name
+                     << ". There may be devices referencing itself. Check `dmctl list devices -v`.";
+
+    auto& dm = DeviceMapper::Instance();
+    auto table = GetTableInfoIfExists(dev_name);
+    if (table.empty()) {
+        PCHECK(dm.DeleteDeviceIfExists(dev_name)) << dev_name;
+        return;
+    }
+
+    if (!known_allow_delete) {
+        for (const auto& target : table) {
+            auto base_device_string = GetLinearBaseDeviceString(target);
+            if (base_device_string.empty()) continue;
+            if (ShouldDeleteLoopDevice(
+                        StringPrintf("/sys/dev/block/%s", base_device_string.data()))) {
+                known_allow_delete = true;
+                break;
+            }
+        }
+    }
+    if (!known_allow_delete) {
+        return;
+    }
+
+    std::string dev_string;
+    PCHECK(dm.GetDeviceString(dev_name, &dev_string));
+
+    std::vector<DeviceMapper::DmBlockDevice> devices;
+    PCHECK(dm.GetAvailableDevices(&devices));
+    for (const auto& child_dev : devices) {
+        auto child_bases = GetAllBaseDeviceStrings(child_dev.name());
+        if (child_bases.find(dev_string) != child_bases.end()) {
+            CheckDeleteDeviceMapperTree(child_dev.name(), true /* known_allow_delete */, depth - 1);
+        }
+    }
+
+    PCHECK(dm.DeleteDeviceIfExists(dev_name)) << dev_name;
+}
+
+// Attempt to clean up residues from previous runs.
+void CheckCleanupDeviceMapperDevices() {
+    auto& dm = DeviceMapper::Instance();
+    std::vector<DeviceMapper::DmBlockDevice> devices;
+    PCHECK(dm.GetAvailableDevices(&devices));
+
+    for (const auto& dev : devices) {
+        CheckDeleteDeviceMapperTree(dev.name());
+    }
+}
+
+void CheckUmount(const std::string& path) {
+    PCHECK(TEMP_FAILURE_RETRY(umount(path.data()) == 0) || errno == ENOENT || errno == EINVAL)
+            << path;
+}
+
+void CheckDetachLoopDevices(const std::set<std::string>& exclude_names = {}) {
+    // ~SnapshotFuzzEnv automatically does the following.
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(BLOCK_SYSFS), closedir);
+    PCHECK(dir != nullptr) << BLOCK_SYSFS;
+    LoopControl loop_control;
+    dirent* dp;
+    while ((dp = readdir(dir.get())) != nullptr) {
+        if (exclude_names.find(dp->d_name) != exclude_names.end()) {
+            continue;
+        }
+        if (!ShouldDeleteLoopDevice(StringPrintf("%s/%s", BLOCK_SYSFS, dp->d_name).data())) {
+            continue;
+        }
+        PCHECK(loop_control.Detach(StringPrintf("/dev/block/%s", dp->d_name).data()));
+    }
+}
+
+void CheckUmountAll() {
+    CheckUmount(std::string(MNT_DIR) + "/snapshot_fuzz_data");
+    CheckUmount(std::string(MNT_DIR) + "/" + FAKE_ROOT_NAME);
+}
+
 class AutoDeleteDir : public AutoDevice {
   public:
     static std::unique_ptr<AutoDeleteDir> New(const std::string& path) {
@@ -108,9 +255,7 @@
     }
     ~AutoDeleteDir() {
         if (!HasDevice()) return;
-        if (rmdir(name_.c_str()) == -1) {
-            PLOG(ERROR) << "Cannot remove " << name_;
-        }
+        PCHECK(rmdir(name_.c_str()) == 0 || errno == ENOENT) << name_;
     }
 
   private:
@@ -119,6 +264,15 @@
 
 class AutoUnmount : public AutoDevice {
   public:
+    ~AutoUnmount() {
+        if (!HasDevice()) return;
+        CheckUmount(name_);
+    }
+    AutoUnmount(const std::string& path) : AutoDevice(path) {}
+};
+
+class AutoUnmountTmpfs : public AutoUnmount {
+  public:
     static std::unique_ptr<AutoUnmount> New(const std::string& path, uint64_t size) {
         if (mount("tmpfs", path.c_str(), "tmpfs", 0,
                   (void*)StringPrintf("size=%" PRIu64, size).data()) == -1) {
@@ -127,30 +281,20 @@
         }
         return std::unique_ptr<AutoUnmount>(new AutoUnmount(path));
     }
-    ~AutoUnmount() {
-        if (!HasDevice()) return;
-        if (umount(name_.c_str()) == -1) {
-            PLOG(ERROR) << "Cannot umount " << name_;
-        }
-    }
-
   private:
-    AutoUnmount(const std::string& path) : AutoDevice(path) {}
+    using AutoUnmount::AutoUnmount;
 };
 
 // A directory on tmpfs. Upon destruct, it is unmounted and deleted.
 class AutoMemBasedDir : public AutoDevice {
   public:
     static std::unique_ptr<AutoMemBasedDir> New(const std::string& name, uint64_t size) {
-        if (!Mkdir(MNT_DIR)) {
-            return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
-        }
         auto ret = std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(name));
         ret->auto_delete_mount_dir_ = AutoDeleteDir::New(ret->mount_path());
         if (!ret->auto_delete_mount_dir_->HasDevice()) {
             return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
         }
-        ret->auto_umount_mount_point_ = AutoUnmount::New(ret->mount_path(), size);
+        ret->auto_umount_mount_point_ = AutoUnmountTmpfs::New(ret->mount_path(), size);
         if (!ret->auto_umount_mount_point_->HasDevice()) {
             return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
         }
@@ -191,14 +335,39 @@
 };
 
 SnapshotFuzzEnv::SnapshotFuzzEnv() {
+    CheckCleanupDeviceMapperDevices();
+    CheckDetachLoopDevices();
+    CheckUmountAll();
+
     fake_root_ = AutoMemBasedDir::New(FAKE_ROOT_NAME, FAKE_ROOT_SIZE);
     CHECK(fake_root_ != nullptr);
     CHECK(fake_root_->HasDevice());
     loop_control_ = std::make_unique<LoopControl>();
-    mapped_super_ = CheckMapSuper(fake_root_->persist_path(), loop_control_.get(), &fake_super_);
+
+    fake_data_mount_point_ = MNT_DIR + "/snapshot_fuzz_data"s;
+    auto_delete_data_mount_point_ = AutoDeleteDir::New(fake_data_mount_point_);
+    CHECK(auto_delete_data_mount_point_ != nullptr);
+    CHECK(auto_delete_data_mount_point_->HasDevice());
+
+    const auto& fake_persist_path = fake_root_->persist_path();
+    mapped_super_ = CheckMapImage(fake_persist_path + "/super.img", SUPER_IMAGE_SIZE,
+                                  loop_control_.get(), &fake_super_);
+    mapped_data_ = CheckMapImage(fake_persist_path + "/data.img", DATA_IMAGE_SIZE,
+                                 loop_control_.get(), &fake_data_block_device_);
+    mounted_data_ = CheckMountFormatData(fake_data_block_device_, fake_data_mount_point_);
 }
 
-SnapshotFuzzEnv::~SnapshotFuzzEnv() = default;
+SnapshotFuzzEnv::~SnapshotFuzzEnv() {
+    CheckCleanupDeviceMapperDevices();
+    mounted_data_ = nullptr;
+    auto_delete_data_mount_point_ = nullptr;
+    mapped_data_ = nullptr;
+    mapped_super_ = nullptr;
+    CheckDetachLoopDevices();
+    loop_control_ = nullptr;
+    fake_root_ = nullptr;
+    CheckUmountAll();
+}
 
 void CheckZeroFill(const std::string& file, size_t size) {
     std::string zeros(size, '\0');
@@ -208,18 +377,15 @@
 void SnapshotFuzzEnv::CheckSoftReset() {
     fake_root_->CheckSoftReset();
     CheckZeroFill(super(), SUPER_IMAGE_SIZE);
+    CheckCleanupDeviceMapperDevices();
+    CheckDetachLoopDevices({Basename(fake_super_), Basename(fake_data_block_device_)});
 }
 
 std::unique_ptr<IImageManager> SnapshotFuzzEnv::CheckCreateFakeImageManager(
-        const std::string& path) {
-    auto images_dir = path + "/images";
-    auto metadata_dir = images_dir + "/metadata";
-    auto data_dir = images_dir + "/data";
-
-    PCHECK(Mkdir(images_dir));
+        const std::string& metadata_dir, const std::string& data_dir) {
     PCHECK(Mkdir(metadata_dir));
     PCHECK(Mkdir(data_dir));
-    return ImageManager::Open(metadata_dir, data_dir);
+    return SnapshotFuzzImageManager::Open(metadata_dir, data_dir);
 }
 
 // Helper to create a loop device for a file.
@@ -236,36 +402,42 @@
   public:
     AutoDetachLoopDevice(LoopControl* control, const std::string& device)
         : AutoDevice(device), control_(control) {}
-    ~AutoDetachLoopDevice() { control_->Detach(name_); }
+    ~AutoDetachLoopDevice() { PCHECK(control_->Detach(name_)) << name_; }
 
   private:
     LoopControl* control_;
 };
 
-std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMapSuper(const std::string& fake_persist_path,
-                                                           LoopControl* control,
-                                                           std::string* fake_super) {
-    auto super_img = fake_persist_path + "/super.img";
-    CheckZeroFill(super_img, SUPER_IMAGE_SIZE);
-    CheckCreateLoopDevice(control, super_img, 1s, fake_super);
+std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMapImage(const std::string& img_path,
+                                                           uint64_t size, LoopControl* control,
+                                                           std::string* mapped_path) {
+    CheckZeroFill(img_path, size);
+    CheckCreateLoopDevice(control, img_path, 1s, mapped_path);
 
-    return std::make_unique<AutoDetachLoopDevice>(control, *fake_super);
+    return std::make_unique<AutoDetachLoopDevice>(control, *mapped_path);
 }
 
-std::unique_ptr<ISnapshotManager> SnapshotFuzzEnv::CheckCreateSnapshotManager(
-        const SnapshotFuzzData& data) {
+SnapshotTestModule SnapshotFuzzEnv::CheckCreateSnapshotManager(const SnapshotFuzzData& data) {
+    SnapshotTestModule ret;
     auto partition_opener = std::make_unique<TestPartitionOpener>(super());
+    ret.opener = partition_opener.get();
     CheckWriteSuperMetadata(data, *partition_opener);
     auto metadata_dir = fake_root_->tmp_path() + "/snapshot_metadata";
     PCHECK(Mkdir(metadata_dir));
+    if (data.has_metadata_snapshots_dir()) {
+        PCHECK(Mkdir(metadata_dir + "/snapshots"));
+    }
 
-    auto device_info = new SnapshotFuzzDeviceInfo(data.device_info_data(),
-                                                  std::move(partition_opener), metadata_dir);
-    auto snapshot = SnapshotManager::New(device_info /* takes ownership */);
-    snapshot->images_ = CheckCreateFakeImageManager(fake_root_->tmp_path());
+    ret.device_info = new SnapshotFuzzDeviceInfo(data.device_info_data(),
+                                                 std::move(partition_opener), metadata_dir);
+    auto snapshot = SnapshotManager::New(ret.device_info /* takes ownership */);
+    snapshot->images_ =
+            CheckCreateFakeImageManager(fake_root_->tmp_path() + "/images_manager_metadata",
+                                        fake_data_mount_point_ + "/image_manager_data");
     snapshot->has_local_image_manager_ = data.manager_data().is_local_image_manager();
+    ret.snapshot = std::move(snapshot);
 
-    return snapshot;
+    return ret;
 }
 
 const std::string& SnapshotFuzzEnv::super() const {
@@ -311,4 +483,34 @@
     CHECK(FlashPartitionTable(opener, super(), *metadata.get()));
 }
 
+std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMountFormatData(const std::string& blk_device,
+                                                                  const std::string& mount_point) {
+    FstabEntry entry{
+            .blk_device = blk_device,
+            .length = static_cast<off64_t>(DATA_IMAGE_SIZE),
+            .fs_type = "ext4",
+            .mount_point = mount_point,
+    };
+    CHECK(0 == fs_mgr_do_format(entry, false /* crypt_footer */));
+    CHECK(0 == fs_mgr_do_mount_one(entry));
+    return std::make_unique<AutoUnmount>(mount_point);
+}
+
+SnapshotFuzzImageManager::~SnapshotFuzzImageManager() {
+    // Remove relevant gsid.mapped_images.* props.
+    for (const auto& name : mapped_) {
+        CHECK(UnmapImageIfExists(name)) << "Cannot unmap " << name;
+    }
+}
+
+bool SnapshotFuzzImageManager::MapImageDevice(const std::string& name,
+                                              const std::chrono::milliseconds& timeout_ms,
+                                              std::string* path) {
+    if (impl_->MapImageDevice(name, timeout_ms, path)) {
+        mapped_.insert(name);
+        return true;
+    }
+    return false;
+}
+
 }  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index 5533def..fa327b8 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <memory>
+#include <set>
 #include <string>
 
 #include <android-base/file.h>
@@ -31,12 +33,19 @@
 namespace android::snapshot {
 
 class AutoMemBasedDir;
+class SnapshotFuzzDeviceInfo;
 
 class DummyAutoDevice : public AutoDevice {
   public:
     DummyAutoDevice(bool mounted) : AutoDevice(mounted ? "dummy" : "") {}
 };
 
+struct SnapshotTestModule {
+    std::unique_ptr<ISnapshotManager> snapshot;
+    SnapshotFuzzDeviceInfo* device_info = nullptr;
+    TestPartitionOpener* opener = nullptr;
+};
+
 // Prepare test environment. This has a heavy overhead and should be done once.
 class SnapshotFuzzEnv {
   public:
@@ -54,7 +63,7 @@
     // Create a snapshot manager for this test run.
     // Client is responsible for maintaining the lifetime of |data| over the life time of
     // ISnapshotManager.
-    std::unique_ptr<ISnapshotManager> CheckCreateSnapshotManager(const SnapshotFuzzData& data);
+    SnapshotTestModule CheckCreateSnapshotManager(const SnapshotFuzzData& data);
 
     // Return path to super partition.
     const std::string& super() const;
@@ -62,14 +71,22 @@
   private:
     std::unique_ptr<AutoMemBasedDir> fake_root_;
     std::unique_ptr<android::dm::LoopControl> loop_control_;
+    std::string fake_data_mount_point_;
+    std::unique_ptr<AutoDevice> auto_delete_data_mount_point_;
     std::unique_ptr<AutoDevice> mapped_super_;
     std::string fake_super_;
+    std::unique_ptr<AutoDevice> mapped_data_;
+    std::string fake_data_block_device_;
+    std::unique_ptr<AutoDevice> mounted_data_;
 
     static std::unique_ptr<android::fiemap::IImageManager> CheckCreateFakeImageManager(
-            const std::string& fake_tmp_path);
-    static std::unique_ptr<AutoDevice> CheckMapSuper(const std::string& fake_persist_path,
+            const std::string& metadata_dir, const std::string& data_dir);
+    static std::unique_ptr<AutoDevice> CheckMapImage(const std::string& fake_persist_path,
+                                                     uint64_t size,
                                                      android::dm::LoopControl* control,
-                                                     std::string* fake_super);
+                                                     std::string* mapped_path);
+    static std::unique_ptr<AutoDevice> CheckMountFormatData(const std::string& blk_device,
+                                                            const std::string& mount_point);
 
     void CheckWriteSuperMetadata(const SnapshotFuzzData& proto,
                                  const android::fs_mgr::IPartitionOpener& opener);
@@ -97,10 +114,8 @@
     }
 
     // Following APIs are fuzzed.
-    std::string GetSlotSuffix() const override { return data_->slot_suffix_is_a() ? "_a" : "_b"; }
-    std::string GetOtherSlotSuffix() const override {
-        return data_->slot_suffix_is_a() ? "_b" : "_a";
-    }
+    std::string GetSlotSuffix() const override { return CurrentSlotIsA() ? "_a" : "_b"; }
+    std::string GetOtherSlotSuffix() const override { return CurrentSlotIsA() ? "_b" : "_a"; }
     bool IsOverlayfsSetup() const override { return data_->is_overlayfs_setup(); }
     bool SetBootControlMergeStatus(android::hardware::boot::V1_1::MergeStatus) override {
         return data_->allow_set_boot_control_merge_status();
@@ -110,10 +125,79 @@
     }
     bool IsRecovery() const override { return data_->is_recovery(); }
 
+    void SwitchSlot() { switched_slot_ = !switched_slot_; }
+
   private:
     const FuzzDeviceInfoData* data_;
     std::unique_ptr<TestPartitionOpener> partition_opener_;
     std::string metadata_dir_;
+    bool switched_slot_ = false;
+
+    bool CurrentSlotIsA() const { return data_->slot_suffix_is_a() != switched_slot_; }
+};
+
+// A spy class on ImageManager implementation. Upon destruction, unmaps all images
+// map through this object.
+class SnapshotFuzzImageManager : public android::fiemap::IImageManager {
+  public:
+    static std::unique_ptr<SnapshotFuzzImageManager> Open(const std::string& metadata_dir,
+                                                          const std::string& data_dir) {
+        auto impl = android::fiemap::ImageManager::Open(metadata_dir, data_dir);
+        if (impl == nullptr) return nullptr;
+        return std::unique_ptr<SnapshotFuzzImageManager>(
+                new SnapshotFuzzImageManager(std::move(impl)));
+    }
+
+    ~SnapshotFuzzImageManager();
+
+    // Spied APIs.
+    bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
+                        std::string* path) override;
+
+    // Other functions call through.
+    android::fiemap::FiemapStatus CreateBackingImage(
+            const std::string& name, uint64_t size, int flags,
+            std::function<bool(uint64_t, uint64_t)>&& on_progress) override {
+        return impl_->CreateBackingImage(name, size, flags, std::move(on_progress));
+    }
+    bool DeleteBackingImage(const std::string& name) override {
+        return impl_->DeleteBackingImage(name);
+    }
+    bool UnmapImageDevice(const std::string& name) override {
+        return impl_->UnmapImageDevice(name);
+    }
+    bool BackingImageExists(const std::string& name) override {
+        return impl_->BackingImageExists(name);
+    }
+    bool IsImageMapped(const std::string& name) override { return impl_->IsImageMapped(name); }
+    bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
+                                  std::string* dev) override {
+        return impl_->MapImageWithDeviceMapper(opener, name, dev);
+    }
+    bool GetMappedImageDevice(const std::string& name, std::string* device) override {
+        return impl_->GetMappedImageDevice(name, device);
+    }
+    bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override {
+        return impl_->MapAllImages(init);
+    }
+    bool DisableImage(const std::string& name) override { return impl_->DisableImage(name); }
+    bool RemoveDisabledImages() override { return impl_->RemoveDisabledImages(); }
+    std::vector<std::string> GetAllBackingImages() override { return impl_->GetAllBackingImages(); }
+    android::fiemap::FiemapStatus ZeroFillNewImage(const std::string& name,
+                                                   uint64_t bytes) override {
+        return impl_->ZeroFillNewImage(name, bytes);
+    }
+    bool RemoveAllImages() override { return impl_->RemoveAllImages(); }
+    bool UnmapImageIfExists(const std::string& name) override {
+        return impl_->UnmapImageIfExists(name);
+    }
+
+  private:
+    std::unique_ptr<android::fiemap::IImageManager> impl_;
+    std::set<std::string> mapped_;
+
+    SnapshotFuzzImageManager(std::unique_ptr<android::fiemap::IImageManager>&& impl)
+        : impl_(std::move(impl)) {}
 };
 
 }  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/update_engine/update_metadata.proto b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
index be5e1fe..8a11eaa 100644
--- a/fs_mgr/libsnapshot/update_engine/update_metadata.proto
+++ b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
@@ -45,7 +45,12 @@
 }
 
 message InstallOperation {
-    enum Type { SOURCE_COPY = 4; }
+    enum Type {
+        SOURCE_COPY = 4;
+        // Not used by libsnapshot. Declared here so that the fuzzer has an
+        // alternative value to use for |type|.
+        ZERO = 6;
+    }
     required Type type = 1;
     repeated Extent src_extents = 4;
     repeated Extent dst_extents = 6;
diff --git a/init/README.md b/init/README.md
index 726c0cc..b70366b 100644
--- a/init/README.md
+++ b/init/README.md
@@ -644,7 +644,8 @@
 `wait <path> [ <timeout> ]`
 > Poll for the existence of the given file and return when found,
   or the timeout has been reached. If timeout is not specified it
-  currently defaults to five seconds.
+  currently defaults to five seconds. The timeout value can be
+  fractional seconds, specified in floating point notation.
 
 `wait_for_prop <name> <value>`
 > Wait for system property _name_ to be _value_. Properties are expanded
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 200bfff..149a766 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -49,6 +49,7 @@
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/parsedouble.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -1065,11 +1066,12 @@
 static Result<void> do_wait(const BuiltinArguments& args) {
     auto timeout = kCommandRetryTimeout;
     if (args.size() == 3) {
-        int timeout_int;
-        if (!android::base::ParseInt(args[2], &timeout_int)) {
+        double timeout_double;
+        if (!android::base::ParseDouble(args[2], &timeout_double, 0)) {
             return Error() << "failed to parse timeout";
         }
-        timeout = std::chrono::seconds(timeout_int);
+        timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(
+                std::chrono::duration<double>(timeout_double));
     }
 
     if (wait_for_file(args[1].c_str(), timeout) != 0) {
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
index d62ecb0..d1a84f3 100644
--- a/init/check_builtins.cpp
+++ b/init/check_builtins.cpp
@@ -25,6 +25,7 @@
 #include <sys/time.h>
 
 #include <android-base/logging.h>
+#include <android-base/parsedouble.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 
@@ -205,8 +206,8 @@
 
 Result<void> check_wait(const BuiltinArguments& args) {
     if (args.size() == 3 && !args[2].empty()) {
-        int timeout_int;
-        if (!android::base::ParseInt(args[2], &timeout_int)) {
+        double timeout_double;
+        if (!android::base::ParseDouble(args[2], &timeout_double, 0)) {
             return Error() << "failed to parse timeout";
         }
     }
diff --git a/init/init.cpp b/init/init.cpp
index 3f8f628..631db8e 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -537,7 +537,9 @@
 // Set the UDC controller for the ConfigFS USB Gadgets.
 // Read the UDC controller in use from "/sys/class/udc".
 // In case of multiple UDC controllers select the first one.
-static void set_usb_controller() {
+static void SetUsbController() {
+    static auto controller_set = false;
+    if (controller_set) return;
     std::unique_ptr<DIR, decltype(&closedir)>dir(opendir("/sys/class/udc"), closedir);
     if (!dir) return;
 
@@ -546,6 +548,7 @@
         if (dp->d_name[0] == '.') continue;
 
         SetProperty("sys.usb.controller", dp->d_name);
+        controller_set = true;
         break;
     }
 }
@@ -800,7 +803,7 @@
     fs_mgr_vendor_overlay_mount_all();
     export_oem_lock_status();
     MountHandler mount_handler(&epoll);
-    set_usb_controller();
+    SetUsbController();
 
     const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
     Action::set_function_map(&function_map);
@@ -910,6 +913,7 @@
         }
         if (!IsShuttingDown()) {
             HandleControlMessages();
+            SetUsbController();
         }
     }
 
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 842b2e5..82f5b8c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -877,28 +877,32 @@
 }
 
 void PropertyLoadBootDefaults() {
-    // TODO(b/117892318): merge prop.default and build.prop files into one
     // 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
     // property files, regardless of if they are "ro." properties or not.
     std::map<std::string, std::string> properties;
-    if (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {
-        // Try recovery path
-        if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
-            // Try legacy path
-            load_properties_from_file("/default.prop", nullptr, &properties);
-        }
+
+    if (IsRecoveryMode()) {
+        load_properties_from_file("/prop.default", nullptr, &properties);
     }
+
     load_properties_from_file("/system/build.prop", nullptr, &properties);
     load_properties_from_file("/system_ext/build.prop", nullptr, &properties);
-    load_properties_from_file("/vendor/default.prop", nullptr, &properties);
+
+    // TODO(b/117892318): uncomment the following condition when vendor.imgs for
+    // aosp_* targets are all updated.
+//    if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
+        load_properties_from_file("/vendor/default.prop", nullptr, &properties);
+//    }
     load_properties_from_file("/vendor/build.prop", nullptr, &properties);
+
     if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
         load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);
     } else {
         load_properties_from_file("/odm/default.prop", nullptr, &properties);
         load_properties_from_file("/odm/build.prop", nullptr, &properties);
     }
+
     load_properties_from_file("/product/build.prop", nullptr, &properties);
     load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
 
diff --git a/init/test_kill_services/AndroidTest.xml b/init/test_kill_services/AndroidTest.xml
index c1dcd59..8018efa 100644
--- a/init/test_kill_services/AndroidTest.xml
+++ b/init/test_kill_services/AndroidTest.xml
@@ -18,7 +18,16 @@
     <option name="test-suite-tag" value="apct-native" />
 
     <!-- cannot be autogenerated: b/153565474 -->
-    <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer" />
+    <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer">
+        <!-- flake mitigation, in case device is in bad state-->
+        <option name="pre-reboot" value="true" />
+        <!-- sometimes device gets into bad state, and we don't detect it in this test,
+          so the test succeeds and the next test fails. This is a really bad result, so
+          to avoid that, making sure we reboot the device again before running any more
+          tests.
+          TODO(b/152556737): add metrics for successful device recovery -->
+        <option name="post-reboot" value="true" />
+    </target_preparer>
 
     <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
 
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 96a5b55..565e7d4 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -61,8 +61,8 @@
 
 TEST(util, ReadFileSymbolicLink) {
     errno = 0;
-    // lrw------- 1 root root 23 2008-12-31 19:00 default.prop -> system/etc/prop.default
-    auto file_contents = ReadFile("/default.prop");
+    // lrwxr-xr-x 1 root shell 6 2009-01-01 09:00 /system/bin/ps -> toybox
+    auto file_contents = ReadFile("/system/bin/ps");
     EXPECT_EQ(ELOOP, errno);
     ASSERT_FALSE(file_contents.ok());
     EXPECT_EQ("open() failed: Too many symbolic links encountered",
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index dc989a0..c4cfa6f 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -48,6 +48,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "apex_inherit",
 }
 
 cc_defaults {
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index 24b88d2..23d76f4 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -112,7 +112,6 @@
 
 /* Multiple log_id_t opens */
 struct logger* android_logger_open(struct logger_list* logger_list, log_id_t id);
-#define android_logger_close android_logger_free
 /* Single log_id_t open */
 struct logger_list* android_logger_list_open(log_id_t id, int mode,
                                              unsigned int tail, pid_t pid);
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 0c9a2b8..15b0d89 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -51,6 +51,12 @@
             enabled: false,
         },
     },
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
 
 // Tests
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index ff886fd..db61669 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -8,6 +8,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "apex_inherit",
     export_include_dirs: ["include"],
 
     target: {
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 627f0d4..3b98bab 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -43,6 +43,7 @@
         "//apex_available:anyapex",
         "//apex_available:platform",
     ],
+    min_sdk_version: "apex_inherit",
 }
 
 cc_test {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index f3d3f27..3c44534 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -146,6 +146,12 @@
             exclude_shared_libs: ["libdexfile_support"],
         },
     },
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
 
 // Static library without DEX support to avoid dependencies on the ART APEX.
@@ -411,6 +417,7 @@
     srcs: [
         "benchmarks/unwind_benchmarks.cpp",
         "benchmarks/ElfBenchmark.cpp",
+        "benchmarks/MapsBenchmark.cpp",
         "benchmarks/SymbolBenchmark.cpp",
         "benchmarks/Utils.cpp",
     ],
diff --git a/libunwindstack/benchmarks/ElfBenchmark.cpp b/libunwindstack/benchmarks/ElfBenchmark.cpp
index c108a2a..a46bd7a 100644
--- a/libunwindstack/benchmarks/ElfBenchmark.cpp
+++ b/libunwindstack/benchmarks/ElfBenchmark.cpp
@@ -23,7 +23,9 @@
 #include <benchmark/benchmark.h>
 
 #include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
 
 #include "Utils.h"
 
@@ -75,3 +77,66 @@
   BenchmarkElfCreate(state, GetCompressedElfFile());
 }
 BENCHMARK(BM_elf_create_compressed);
+
+static void InitializeBuildId(benchmark::State& state, unwindstack::Maps& maps,
+                              unwindstack::MapInfo** build_id_map_info) {
+  if (!maps.Parse()) {
+    state.SkipWithError("Failed to parse local maps.");
+    return;
+  }
+
+  // Find the libc.so share library and use that for benchmark purposes.
+  *build_id_map_info = nullptr;
+  for (auto& map_info : maps) {
+    if (map_info->offset == 0 && map_info->GetBuildID() != "") {
+      *build_id_map_info = map_info.get();
+      break;
+    }
+  }
+
+  if (*build_id_map_info == nullptr) {
+    state.SkipWithError("Failed to find a map with a BuildID.");
+  }
+}
+
+static void BM_elf_get_build_id_from_object(benchmark::State& state) {
+  unwindstack::LocalMaps maps;
+  unwindstack::MapInfo* build_id_map_info;
+  InitializeBuildId(state, maps, &build_id_map_info);
+
+  unwindstack::Elf* elf = build_id_map_info->GetElf(std::shared_ptr<unwindstack::Memory>(),
+                                                    unwindstack::Regs::CurrentArch());
+  if (!elf->valid()) {
+    state.SkipWithError("Cannot get valid elf from map.");
+  }
+
+  for (auto _ : state) {
+    state.PauseTiming();
+    uintptr_t id = build_id_map_info->build_id;
+    if (id != 0) {
+      delete reinterpret_cast<std::string*>(id);
+      build_id_map_info->build_id = 0;
+    }
+    state.ResumeTiming();
+    benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
+  }
+}
+BENCHMARK(BM_elf_get_build_id_from_object);
+
+static void BM_elf_get_build_id_from_file(benchmark::State& state) {
+  unwindstack::LocalMaps maps;
+  unwindstack::MapInfo* build_id_map_info;
+  InitializeBuildId(state, maps, &build_id_map_info);
+
+  for (auto _ : state) {
+    state.PauseTiming();
+    uintptr_t id = build_id_map_info->build_id;
+    if (id != 0) {
+      delete reinterpret_cast<std::string*>(id);
+      build_id_map_info->build_id = 0;
+    }
+    state.ResumeTiming();
+    benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
+  }
+}
+BENCHMARK(BM_elf_get_build_id_from_file);
diff --git a/libunwindstack/benchmarks/MapsBenchmark.cpp b/libunwindstack/benchmarks/MapsBenchmark.cpp
new file mode 100644
index 0000000..be106a3
--- /dev/null
+++ b/libunwindstack/benchmarks/MapsBenchmark.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 <err.h>
+#include <stdint.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Maps.h>
+
+class BenchmarkLocalUpdatableMaps : public unwindstack::LocalUpdatableMaps {
+ public:
+  BenchmarkLocalUpdatableMaps() : unwindstack::LocalUpdatableMaps() {}
+  virtual ~BenchmarkLocalUpdatableMaps() = default;
+
+  const std::string GetMapsFile() const override { return maps_file_; }
+
+  void BenchmarkSetMapsFile(const std::string& maps_file) { maps_file_ = maps_file; }
+
+ private:
+  std::string maps_file_;
+};
+
+constexpr size_t kNumMaps = 10000;
+
+static void CreateInitialMap(const char* filename) {
+  std::string maps;
+  for (size_t i = 0; i < kNumMaps; i += 2) {
+    maps += android::base::StringPrintf("%zu-%zu r-xp 0000 00:00 0 name%zu\n", i * 1000,
+                                        (i + 1) * 1000, i);
+  }
+  if (!android::base::WriteStringToFile(maps, filename)) {
+    errx(1, "WriteStringToFile failed");
+  }
+}
+
+static void CreateReparseMap(const char* filename) {
+  std::string maps;
+  for (size_t i = 0; i < kNumMaps; i++) {
+    maps += android::base::StringPrintf("%zu-%zu r-xp 0000 00:00 0 name%zu\n", i * 2000,
+                                        (i + 1) * 2000, 2 * i);
+  }
+  if (!android::base::WriteStringToFile(maps, filename)) {
+    errx(1, "WriteStringToFile failed");
+  }
+}
+
+void BM_local_updatable_maps_reparse(benchmark::State& state) {
+  TemporaryFile initial_map;
+  CreateInitialMap(initial_map.path);
+
+  TemporaryFile reparse_map;
+  CreateReparseMap(reparse_map.path);
+
+  for (auto _ : state) {
+    BenchmarkLocalUpdatableMaps maps;
+    maps.BenchmarkSetMapsFile(initial_map.path);
+    if (!maps.Reparse()) {
+      errx(1, "Internal Error: reparse of initial maps filed.");
+    }
+    if (maps.Total() != (kNumMaps / 2)) {
+      errx(1, "Internal Error: Incorrect total number of maps %zu, expected %zu.", maps.Total(),
+           kNumMaps / 2);
+    }
+    maps.BenchmarkSetMapsFile(reparse_map.path);
+    if (!maps.Reparse()) {
+      errx(1, "Internal Error: reparse of second set of maps filed.");
+    }
+    if (maps.Total() != kNumMaps) {
+      errx(1, "Internal Error: Incorrect total number of maps %zu, expected %zu.", maps.Total(),
+           kNumMaps);
+    }
+  }
+}
+BENCHMARK(BM_local_updatable_maps_reparse);
diff --git a/libunwindstack/benchmarks/unwind_benchmarks.cpp b/libunwindstack/benchmarks/unwind_benchmarks.cpp
index de9137a..0bee6ef 100644
--- a/libunwindstack/benchmarks/unwind_benchmarks.cpp
+++ b/libunwindstack/benchmarks/unwind_benchmarks.cpp
@@ -22,7 +22,6 @@
 
 #include <android-base/strings.h>
 
-#include <unwindstack/Elf.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
@@ -83,63 +82,4 @@
 }
 BENCHMARK(BM_cached_unwind);
 
-static void Initialize(benchmark::State& state, unwindstack::Maps& maps,
-                       unwindstack::MapInfo** build_id_map_info) {
-  if (!maps.Parse()) {
-    state.SkipWithError("Failed to parse local maps.");
-    return;
-  }
-
-  // Find the libc.so share library and use that for benchmark purposes.
-  *build_id_map_info = nullptr;
-  for (auto& map_info : maps) {
-    if (map_info->offset == 0 && map_info->GetBuildID() != "") {
-      *build_id_map_info = map_info.get();
-      break;
-    }
-  }
-
-  if (*build_id_map_info == nullptr) {
-    state.SkipWithError("Failed to find a map with a BuildID.");
-  }
-}
-
-static void BM_get_build_id_from_elf(benchmark::State& state) {
-  unwindstack::LocalMaps maps;
-  unwindstack::MapInfo* build_id_map_info;
-  Initialize(state, maps, &build_id_map_info);
-
-  unwindstack::Elf* elf = build_id_map_info->GetElf(std::shared_ptr<unwindstack::Memory>(),
-                                                    unwindstack::Regs::CurrentArch());
-  if (!elf->valid()) {
-    state.SkipWithError("Cannot get valid elf from map.");
-  }
-
-  for (auto _ : state) {
-    uintptr_t id = build_id_map_info->build_id;
-    if (id != 0) {
-      delete reinterpret_cast<std::string*>(id);
-      build_id_map_info->build_id = 0;
-    }
-    benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
-  }
-}
-BENCHMARK(BM_get_build_id_from_elf);
-
-static void BM_get_build_id_from_file(benchmark::State& state) {
-  unwindstack::LocalMaps maps;
-  unwindstack::MapInfo* build_id_map_info;
-  Initialize(state, maps, &build_id_map_info);
-
-  for (auto _ : state) {
-    uintptr_t id = build_id_map_info->build_id;
-    if (id != 0) {
-      delete reinterpret_cast<std::string*>(id);
-      build_id_map_info->build_id = 0;
-    }
-    benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
-  }
-}
-BENCHMARK(BM_get_build_id_from_file);
-
 BENCHMARK_MAIN();
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 3ab619b..0f7044a 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -22,6 +22,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "apex_inherit",
 
     header_libs: [
         "liblog_headers",
@@ -162,6 +163,7 @@
         "//apex_available:anyapex",
         "//apex_available:platform",
     ],
+    min_sdk_version: "apex_inherit",
 }
 
 cc_library {
@@ -173,8 +175,8 @@
     ],
 
     shared_libs: [
-         "libutils",
-         "libbacktrace",
+        "libutils",
+        "libbacktrace",
     ],
 
     target: {
@@ -192,6 +194,45 @@
     },
 }
 
+cc_defaults {
+    name: "libutils_fuzz_defaults",
+    host_supported: true,
+    shared_libs: [
+        "libutils",
+        "libbase",
+    ],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_bitset",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["BitSet_fuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_filemap",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["FileMap_fuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_string8",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["String8_fuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_string16",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["String16_fuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_vector",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["Vector_fuzz.cpp"],
+}
+
 cc_test {
     name: "libutils_test",
     host_supported: true,
diff --git a/libutils/BitSet_fuzz.cpp b/libutils/BitSet_fuzz.cpp
new file mode 100644
index 0000000..2e6043c
--- /dev/null
+++ b/libutils/BitSet_fuzz.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+#include <functional>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/BitSet.h"
+static constexpr uint8_t MAX_OPERATIONS = 50;
+
+// We need to handle both 32 and 64 bit bitsets, so we use a function template
+// here. Sadly, std::function can't be generic, so we generate a vector of
+// std::functions using this function.
+template <typename T>
+std::vector<std::function<void(T, uint32_t)>> getOperationsForType() {
+    return {
+            [](T bs, uint32_t val) -> void { bs.markBit(val); },
+            [](T bs, uint32_t val) -> void { bs.valueForBit(val); },
+            [](T bs, uint32_t val) -> void { bs.hasBit(val); },
+            [](T bs, uint32_t val) -> void { bs.clearBit(val); },
+            [](T bs, uint32_t val) -> void { bs.getIndexOfBit(val); },
+            [](T bs, uint32_t) -> void { bs.clearFirstMarkedBit(); },
+            [](T bs, uint32_t) -> void { bs.markFirstUnmarkedBit(); },
+            [](T bs, uint32_t) -> void { bs.clearLastMarkedBit(); },
+            [](T bs, uint32_t) -> void { bs.clear(); },
+            [](T bs, uint32_t) -> void { bs.count(); },
+            [](T bs, uint32_t) -> void { bs.isEmpty(); },
+            [](T bs, uint32_t) -> void { bs.isFull(); },
+            [](T bs, uint32_t) -> void { bs.firstMarkedBit(); },
+            [](T bs, uint32_t) -> void { bs.lastMarkedBit(); },
+    };
+}
+
+// Our operations for 32 and 64 bit bitsets
+static const std::vector<std::function<void(android::BitSet32, uint32_t)>> thirtyTwoBitOps =
+        getOperationsForType<android::BitSet32>();
+static const std::vector<std::function<void(android::BitSet64, uint32_t)>> sixtyFourBitOps =
+        getOperationsForType<android::BitSet64>();
+
+void runOperationFor32Bit(android::BitSet32 bs, uint32_t bit, uint8_t operation) {
+    thirtyTwoBitOps[operation](bs, bit);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider dataProvider(data, size);
+    uint32_t thirty_two_base = dataProvider.ConsumeIntegral<uint32_t>();
+    uint64_t sixty_four_base = dataProvider.ConsumeIntegral<uint64_t>();
+    android::BitSet32 b1 = android::BitSet32(thirty_two_base);
+    android::BitSet64 b2 = android::BitSet64(sixty_four_base);
+
+    size_t opsRun = 0;
+    while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+        uint32_t bit = dataProvider.ConsumeIntegral<uint32_t>();
+        uint8_t op = dataProvider.ConsumeIntegral<uint8_t>();
+        thirtyTwoBitOps[op % thirtyTwoBitOps.size()](b1, bit);
+        sixtyFourBitOps[op % sixtyFourBitOps.size()](b2, bit);
+    }
+    return 0;
+}
diff --git a/libutils/FileMap_fuzz.cpp b/libutils/FileMap_fuzz.cpp
new file mode 100644
index 0000000..d800564
--- /dev/null
+++ b/libutils/FileMap_fuzz.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+#include <iostream>
+
+#include "android-base/file.h"
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/FileMap.h"
+
+static constexpr uint16_t MAX_STR_SIZE = 256;
+static constexpr uint8_t MAX_FILENAME_SIZE = 32;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider dataProvider(data, size);
+    TemporaryFile tf;
+    // Generate file contents
+    std::string contents = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);
+    // If we have string contents, dump them into the file.
+    // Otherwise, just leave it as an empty file.
+    if (contents.length() > 0) {
+        const char* bytes = contents.c_str();
+        android::base::WriteStringToFd(bytes, tf.fd);
+    }
+    android::FileMap m;
+    // Generate create() params
+    std::string orig_name = dataProvider.ConsumeRandomLengthString(MAX_FILENAME_SIZE);
+    size_t length = dataProvider.ConsumeIntegralInRange<size_t>(1, SIZE_MAX);
+    off64_t offset = dataProvider.ConsumeIntegralInRange<off64_t>(1, INT64_MAX);
+    bool read_only = dataProvider.ConsumeBool();
+    m.create(orig_name.c_str(), tf.fd, offset, length, read_only);
+    m.getDataOffset();
+    m.getFileName();
+    m.getDataLength();
+    m.getDataPtr();
+    int enum_index = dataProvider.ConsumeIntegral<int>();
+    m.advise(static_cast<android::FileMap::MapAdvice>(enum_index));
+    return 0;
+}
diff --git a/libutils/String16_fuzz.cpp b/libutils/String16_fuzz.cpp
new file mode 100644
index 0000000..63c2800
--- /dev/null
+++ b/libutils/String16_fuzz.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+#include <iostream>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/String16.h"
+static constexpr int MAX_STRING_BYTES = 256;
+static constexpr uint8_t MAX_OPERATIONS = 50;
+
+std::vector<std::function<void(FuzzedDataProvider&, android::String16, android::String16)>>
+        operations = {
+
+                // Bytes and size
+                ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
+                    str1.string();
+                }),
+                ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
+                    str1.isStaticString();
+                }),
+                ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
+                    str1.size();
+                }),
+
+                // Casing
+                ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
+                    str1.makeLower();
+                }),
+
+                // Comparison
+                ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
+                    str1.startsWith(str2);
+                }),
+                ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
+                    str1.contains(str2.string());
+                }),
+                ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
+                    str1.compare(str2);
+                }),
+
+                // Append and format
+                ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
+                    str1.append(str2);
+                }),
+                ([](FuzzedDataProvider& dataProvider, android::String16 str1,
+                    android::String16 str2) -> void {
+                    int pos = dataProvider.ConsumeIntegralInRange<int>(0, str1.size());
+                    str1.insert(pos, str2.string());
+                }),
+
+                // Find and replace operations
+                ([](FuzzedDataProvider& dataProvider, android::String16 str1,
+                    android::String16) -> void {
+                    char16_t findChar = dataProvider.ConsumeIntegral<char16_t>();
+                    str1.findFirst(findChar);
+                }),
+                ([](FuzzedDataProvider& dataProvider, android::String16 str1,
+                    android::String16) -> void {
+                    char16_t findChar = dataProvider.ConsumeIntegral<char16_t>();
+                    str1.findLast(findChar);
+                }),
+                ([](FuzzedDataProvider& dataProvider, android::String16 str1,
+                    android::String16) -> void {
+                    char16_t findChar = dataProvider.ConsumeIntegral<char16_t>();
+                    char16_t replaceChar = dataProvider.ConsumeIntegral<char16_t>();
+                    str1.replaceAll(findChar, replaceChar);
+                }),
+                ([](FuzzedDataProvider& dataProvider, android::String16 str1,
+                    android::String16) -> void {
+                    size_t len = dataProvider.ConsumeIntegral<size_t>();
+                    size_t begin = dataProvider.ConsumeIntegral<size_t>();
+                    str1.remove(len, begin);
+                }),
+};
+
+void callFunc(uint8_t index, FuzzedDataProvider& dataProvider, android::String16 str1,
+              android::String16 str2) {
+    operations[index](dataProvider, str1, str2);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider dataProvider(data, size);
+    // We're generating two char vectors.
+    // First, generate lengths.
+    const size_t kVecOneLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);
+    const size_t kVecTwoLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);
+
+    // Next, populate the vectors
+    std::vector<char> vec = dataProvider.ConsumeBytesWithTerminator<char>(kVecOneLen);
+    std::vector<char> vec_two = dataProvider.ConsumeBytesWithTerminator<char>(kVecTwoLen);
+
+    // Get pointers to their data
+    char* char_one = vec.data();
+    char* char_two = vec_two.data();
+
+    // Create UTF16 representations
+    android::String16 str_one_utf16 = android::String16(char_one);
+    android::String16 str_two_utf16 = android::String16(char_two);
+
+    // Run operations against strings
+    int opsRun = 0;
+    while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+        uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
+        callFunc(op, dataProvider, str_one_utf16, str_two_utf16);
+    }
+
+    str_one_utf16.remove(0, str_one_utf16.size());
+    str_two_utf16.remove(0, str_two_utf16.size());
+    return 0;
+}
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
new file mode 100644
index 0000000..2adfe98
--- /dev/null
+++ b/libutils/String8_fuzz.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+#include <functional>
+#include <iostream>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/String8.h"
+
+static constexpr int MAX_STRING_BYTES = 256;
+static constexpr uint8_t MAX_OPERATIONS = 50;
+
+std::vector<std::function<void(FuzzedDataProvider&, android::String8, android::String8)>>
+        operations = {
+
+                // Bytes and size
+                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+                    str1.bytes();
+                },
+                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+                    str1.isEmpty();
+                },
+                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+                    str1.length();
+                },
+                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+                    str1.size();
+                },
+
+                // Casing
+                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+                    str1.toUpper();
+                },
+                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+                    str1.toLower();
+                },
+
+                [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
+                    str1.removeAll(str2.c_str());
+                },
+                [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
+                    str1.compare(str2);
+                },
+
+                // Append and format
+                [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
+                    str1.append(str2);
+                },
+                [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
+                    str1.appendFormat(str1.c_str(), str2.c_str());
+                },
+                [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
+                    str1.format(str1.c_str(), str2.c_str());
+                },
+
+                // Find operation
+                [](FuzzedDataProvider& dataProvider, android::String8 str1,
+                   android::String8) -> void {
+                    // We need to get a value from our fuzzer here.
+                    int start_index = dataProvider.ConsumeIntegralInRange<int>(0, str1.size());
+                    str1.find(str1.c_str(), start_index);
+                },
+
+                // Path handling
+                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+                    str1.getBasePath();
+                },
+                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+                    str1.getPathExtension();
+                },
+                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+                    str1.getPathLeaf();
+                },
+                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+                    str1.getPathDir();
+                },
+                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+                    str1.convertToResPath();
+                },
+                [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+                    android::String8 path_out_str = android::String8();
+                    str1.walkPath(&path_out_str);
+                    path_out_str.clear();
+                },
+                [](FuzzedDataProvider& dataProvider, android::String8 str1,
+                   android::String8) -> void {
+                    str1.setPathName(dataProvider.ConsumeBytesWithTerminator<char>(5).data());
+                },
+                [](FuzzedDataProvider& dataProvider, android::String8 str1,
+                   android::String8) -> void {
+                    str1.appendPath(dataProvider.ConsumeBytesWithTerminator<char>(5).data());
+                },
+};
+
+void callFunc(uint8_t index, FuzzedDataProvider& dataProvider, android::String8 str1,
+              android::String8 str2) {
+    operations[index](dataProvider, str1, str2);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider dataProvider(data, size);
+    // Generate vector lengths
+    const size_t kVecOneLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);
+    const size_t kVecTwoLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);
+    // Populate vectors
+    std::vector<char> vec = dataProvider.ConsumeBytesWithTerminator<char>(kVecOneLen);
+    std::vector<char> vec_two = dataProvider.ConsumeBytesWithTerminator<char>(kVecTwoLen);
+    // Create UTF-8 pointers
+    android::String8 str_one_utf8 = android::String8(vec.data());
+    android::String8 str_two_utf8 = android::String8(vec_two.data());
+
+    // Run operations against strings
+    int opsRun = 0;
+    while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+        uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
+        callFunc(op, dataProvider, str_one_utf8, str_two_utf8);
+    }
+
+    // Just to be extra sure these can be freed, we're going to explicitly clear
+    // them
+    str_one_utf8.clear();
+    str_two_utf8.clear();
+    return 0;
+}
diff --git a/libutils/Vector_fuzz.cpp b/libutils/Vector_fuzz.cpp
new file mode 100644
index 0000000..f6df051
--- /dev/null
+++ b/libutils/Vector_fuzz.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/Vector.h"
+static constexpr uint16_t MAX_VEC_SIZE = 5000;
+
+void runVectorFuzz(const uint8_t* data, size_t size) {
+    FuzzedDataProvider dataProvider(data, size);
+    android::Vector<uint8_t> vec = android::Vector<uint8_t>();
+    // We want to test handling of sizeof as well.
+    android::Vector<uint32_t> vec32 = android::Vector<uint32_t>();
+
+    // We're going to generate two vectors of this size
+    size_t vectorSize = dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE);
+    vec.setCapacity(vectorSize);
+    vec32.setCapacity(vectorSize);
+    for (size_t i = 0; i < vectorSize; i++) {
+        uint8_t count = dataProvider.ConsumeIntegralInRange<uint8_t>(1, 5);
+        vec.insertAt((uint8_t)i, i, count);
+        vec32.insertAt((uint32_t)i, i, count);
+        vec.push_front(i);
+        vec32.push(i);
+    }
+
+    // Now we'll perform some test operations with any remaining data
+    // Index to perform operations at
+    size_t index = dataProvider.ConsumeIntegralInRange<size_t>(0, vec.size());
+    std::vector<uint8_t> remainingVec = dataProvider.ConsumeRemainingBytes<uint8_t>();
+    // Insert an array and vector
+    vec.insertArrayAt(remainingVec.data(), index, remainingVec.size());
+    android::Vector<uint8_t> vecCopy = android::Vector<uint8_t>(vec);
+    vec.insertVectorAt(vecCopy, index);
+    // Same thing for 32 bit vector
+    android::Vector<uint32_t> vec32Copy = android::Vector<uint32_t>(vec32);
+    vec32.insertArrayAt(vec32Copy.array(), index, vec32.size());
+    vec32.insertVectorAt(vec32Copy, index);
+    // Replace single character
+    if (remainingVec.size() > 0) {
+        vec.replaceAt(remainingVec[0], index);
+        vec32.replaceAt(static_cast<uint32_t>(remainingVec[0]), index);
+    } else {
+        vec.replaceAt(0, index);
+        vec32.replaceAt(0, index);
+    }
+    // Add any remaining bytes
+    for (uint8_t i : remainingVec) {
+        vec.add(i);
+        vec32.add(static_cast<uint32_t>(i));
+    }
+    // Shrink capactiy
+    vec.setCapacity(remainingVec.size());
+    vec32.setCapacity(remainingVec.size());
+    // Iterate through each pointer
+    size_t sum = 0;
+    for (auto& it : vec) {
+        sum += it;
+    }
+    for (auto& it : vec32) {
+        sum += it;
+    }
+    // Cleanup
+    vec.clear();
+    vecCopy.clear();
+    vec32.clear();
+    vec32Copy.clear();
+}
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    runVectorFuzz(data, size);
+    return 0;
+}
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 786e7b3..c5a968a 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -111,6 +111,12 @@
             enabled: true,
         },
     },
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.art.debug",
+        "com.android.art.release",
+    ],
 }
 
 // Tests.
diff --git a/logd/Android.bp b/logd/Android.bp
index 3df59f5..d79e258 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -28,41 +28,53 @@
     "-DLIBLOG_LOG_TAG=1006",
 ]
 
-cc_library_static {
-    name: "liblogd",
-
-    srcs: [
-        "LogCommand.cpp",
-        "CommandListener.cpp",
-        "LogListener.cpp",
-        "LogReader.cpp",
-        "LogReaderThread.cpp",
-        "LogBuffer.cpp",
-        "LogBufferElement.cpp",
-        "LogStatistics.cpp",
-        "LogWhiteBlackList.cpp",
-        "libaudit.c",
-        "LogAudit.cpp",
-        "LogKlog.cpp",
-        "LogTags.cpp",
-    ],
-    logtags: ["event.logtags"],
+cc_defaults {
+    name: "logd_defaults",
 
     shared_libs: ["libbase"],
-
-    export_include_dirs: ["."],
-
     cflags: [
         "-Wextra",
         "-Wthread-safety",
     ] + event_flag,
+
+    lto: {
+        thin: true,
+    },
+}
+
+cc_library_static {
+    name: "liblogd",
+    defaults: ["logd_defaults"],
+
+    srcs: [
+        "ChattyLogBuffer.cpp",
+        "LogReaderList.cpp",
+        "LogReaderThread.cpp",
+        "LogBufferElement.cpp",
+        "LogStatistics.cpp",
+        "LogWhiteBlackList.cpp",
+        "LogTags.cpp",
+    ],
+    logtags: ["event.logtags"],
+
+    export_include_dirs: ["."],
 }
 
 cc_binary {
     name: "logd",
+    defaults: ["logd_defaults"],
     init_rc: ["logd.rc"],
 
-    srcs: ["main.cpp"],
+    srcs: [
+        "main.cpp",
+        "LogPermissions.cpp",
+        "CommandListener.cpp",
+        "LogListener.cpp",
+        "LogReader.cpp",
+        "LogAudit.cpp",
+        "LogKlog.cpp",
+        "libaudit.cpp",
+    ],
 
     static_libs: [
         "liblog",
@@ -72,30 +84,23 @@
     shared_libs: [
         "libsysutils",
         "libcutils",
-        "libbase",
         "libpackagelistparser",
         "libprocessgroup",
         "libcap",
     ],
-
-    cflags: [
-        "-Wextra",
-    ],
 }
 
 cc_binary {
     name: "auditctl",
 
-    srcs: ["auditctl.cpp"],
-
-    static_libs: [
-        "liblogd",
+    srcs: [
+        "auditctl.cpp",
+        "libaudit.cpp",
     ],
 
     shared_libs: ["libbase"],
 
     cflags: [
-        "-Wconversion",
         "-Wextra",
     ],
 }
diff --git a/logd/LogBuffer.cpp b/logd/ChattyLogBuffer.cpp
similarity index 80%
rename from logd/LogBuffer.cpp
rename to logd/ChattyLogBuffer.cpp
index 4fce751..c6c9a7c 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/ChattyLogBuffer.cpp
@@ -13,9 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// for manual checking of stale entries during LogBuffer::erase()
+// for manual checking of stale entries during ChattyLogBuffer::erase()
 //#define DEBUG_CHECK_FOR_STALE_ENTRIES
 
+#include "ChattyLogBuffer.h"
+
 #include <ctype.h>
 #include <endian.h>
 #include <errno.h>
@@ -29,12 +31,8 @@
 #include <unordered_map>
 #include <utility>
 
-#include <cutils/properties.h>
 #include <private/android_logger.h>
 
-#include "LogBuffer.h"
-#include "LogKlog.h"
-#include "LogReader.h"
 #include "LogUtils.h"
 
 #ifndef __predict_false
@@ -44,27 +42,22 @@
 // Default
 #define log_buffer_size(id) mMaxSize[id]
 
-void LogBuffer::init() {
+void ChattyLogBuffer::Init() {
     log_id_for_each(i) {
-        if (setSize(i, __android_logger_get_buffer_size(i))) {
-            setSize(i, LOG_BUFFER_MIN_SIZE);
+        if (SetSize(i, __android_logger_get_buffer_size(i))) {
+            SetSize(i, LOG_BUFFER_MIN_SIZE);
         }
     }
     // Release any sleeping reader threads to dump their current content.
-    LogReaderThread::wrlock();
-
-    LastLogTimes::iterator times = mTimes.begin();
-    while (times != mTimes.end()) {
-        LogReaderThread* entry = times->get();
-        entry->triggerReader_Locked();
-        times++;
+    auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
+    for (const auto& reader_thread : reader_list_->reader_threads()) {
+        reader_thread->triggerReader_Locked();
     }
-
-    LogReaderThread::unlock();
 }
 
-LogBuffer::LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune, LogStatistics* stats)
-    : mTimes(*times), tags_(tags), prune_(prune), stats_(stats) {
+ChattyLogBuffer::ChattyLogBuffer(LogReaderList* reader_list, LogTags* tags, PruneList* prune,
+                                 LogStatistics* stats)
+    : reader_list_(reader_list), tags_(tags), prune_(prune), stats_(stats) {
     pthread_rwlock_init(&mLogElementsLock, nullptr);
 
     log_id_for_each(i) {
@@ -72,17 +65,17 @@
         droppedElements[i] = nullptr;
     }
 
-    init();
+    Init();
 }
 
-LogBuffer::~LogBuffer() {
+ChattyLogBuffer::~ChattyLogBuffer() {
     log_id_for_each(i) {
         delete lastLoggedElements[i];
         delete droppedElements[i];
     }
 }
 
-LogBufferElementCollection::iterator LogBuffer::GetOldest(log_id_t log_id) {
+LogBufferElementCollection::iterator ChattyLogBuffer::GetOldest(log_id_t log_id) {
     auto it = mLogElements.begin();
     if (oldest_[log_id]) {
         it = *oldest_[log_id];
@@ -98,8 +91,7 @@
 
 enum match_type { DIFFERENT, SAME, SAME_LIBLOG };
 
-static enum match_type identical(LogBufferElement* elem,
-                                 LogBufferElement* last) {
+static enum match_type identical(LogBufferElement* elem, LogBufferElement* last) {
     // is it mostly identical?
     //  if (!elem) return DIFFERENT;
     ssize_t lenl = elem->getMsgLen();
@@ -113,8 +105,7 @@
     if (elem->getTid() != last->getTid()) return DIFFERENT;
 
     // last is more than a minute old, stop squashing identical messages
-    if (elem->getRealTime().nsec() >
-        (last->getRealTime().nsec() + 60 * NS_PER_SEC))
+    if (elem->getRealTime().nsec() > (last->getRealTime().nsec() + 60 * NS_PER_SEC))
         return DIFFERENT;
 
     // Identical message
@@ -123,21 +114,17 @@
     if (lenl == lenr) {
         if (!fastcmp<memcmp>(msgl, msgr, lenl)) return SAME;
         // liblog tagged messages (content gets summed)
-        if ((elem->getLogId() == LOG_ID_EVENTS) &&
-            (lenl == sizeof(android_log_event_int_t)) &&
-            !fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_int_t) -
-                                             sizeof(int32_t)) &&
-            (elem->getTag() == LIBLOG_LOG_TAG)) {
+        if (elem->getLogId() == LOG_ID_EVENTS && lenl == sizeof(android_log_event_int_t) &&
+            !fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_int_t) - sizeof(int32_t)) &&
+            elem->getTag() == LIBLOG_LOG_TAG) {
             return SAME_LIBLOG;
         }
     }
 
     // audit message (except sequence number) identical?
-    if (last->isBinary() &&
-        (lenl > static_cast<ssize_t>(sizeof(android_log_event_string_t))) &&
-        (lenr > static_cast<ssize_t>(sizeof(android_log_event_string_t)))) {
-        if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) -
-                                            sizeof(int32_t))) {
+    if (last->isBinary() && lenl > static_cast<ssize_t>(sizeof(android_log_event_string_t)) &&
+        lenr > static_cast<ssize_t>(sizeof(android_log_event_string_t))) {
+        if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) - sizeof(int32_t))) {
             return DIFFERENT;
         }
         msgl += sizeof(android_log_event_string_t);
@@ -153,15 +140,14 @@
     if (!avcr) return DIFFERENT;
     lenr -= avcr - msgr;
     if (lenl != lenr) return DIFFERENT;
-    if (fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc),
-                        lenl - strlen(avc))) {
+    if (fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc), lenl - strlen(avc))) {
         return DIFFERENT;
     }
     return SAME;
 }
 
-int LogBuffer::log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
-                   pid_t tid, const char* msg, uint16_t len) {
+int ChattyLogBuffer::Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
+                         const char* msg, uint16_t len) {
     if (log_id >= LOG_ID_MAX) {
         return -EINVAL;
     }
@@ -283,8 +269,7 @@
             if (dropped) {
                 // Sum up liblog tag messages?
                 if ((count == 0) /* at Pass 1 */ && (match == SAME_LIBLOG)) {
-                    android_log_event_int_t* event =
-                        reinterpret_cast<android_log_event_int_t*>(
+                    android_log_event_int_t* event = reinterpret_cast<android_log_event_int_t*>(
                             const_cast<char*>(currentLast->getMsg()));
                     //
                     // To unit test, differentiate with something like:
@@ -295,7 +280,7 @@
                     uint32_t swab = event->payload.data;
                     unsigned long long total = htole32(swab);
                     event = reinterpret_cast<android_log_event_int_t*>(
-                        const_cast<char*>(elem->getMsg()));
+                            const_cast<char*>(elem->getMsg()));
                     swab = event->payload.data;
 
                     lastLoggedElements[LOG_ID_EVENTS] = elem;
@@ -350,23 +335,24 @@
     return len;
 }
 
-// assumes LogBuffer::wrlock() held, owns elem, look after garbage collection
-void LogBuffer::log(LogBufferElement* elem) {
+// assumes ChattyLogBuffer::wrlock() held, owns elem, look after garbage collection
+void ChattyLogBuffer::log(LogBufferElement* elem) {
     mLogElements.push_back(elem);
     stats_->Add(elem);
     maybePrune(elem->getLogId());
+    reader_list_->NotifyNewLog(1 << elem->getLogId());
 }
 
-// LogBuffer::wrlock() must be held when this function is called.
-void LogBuffer::maybePrune(log_id_t id) {
+// ChattyLogBuffer::wrlock() must be held when this function is called.
+void ChattyLogBuffer::maybePrune(log_id_t id) {
     unsigned long prune_rows;
     if (stats_->ShouldPrune(id, log_buffer_size(id), &prune_rows)) {
         prune(id, prune_rows);
     }
 }
 
-LogBufferElementCollection::iterator LogBuffer::erase(
-    LogBufferElementCollection::iterator it, bool coalesce) {
+LogBufferElementCollection::iterator ChattyLogBuffer::erase(LogBufferElementCollection::iterator it,
+                                                            bool coalesce) {
     LogBufferElement* element = *it;
     log_id_t id = element->getLogId();
 
@@ -374,9 +360,8 @@
     // after the element is erased from the main logging list.
 
     {  // start of scope for found iterator
-        int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
-                      ? element->getTag()
-                      : element->getUid();
+        int key = (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element->getTag()
+                                                                 : element->getUid();
         LogBufferIteratorMap::iterator found = mLastWorst[id].find(key);
         if ((found != mLastWorst[id].end()) && (it == found->second)) {
             mLastWorst[id].erase(found);
@@ -387,10 +372,8 @@
         // element->getUid() may not be AID_SYSTEM for next-best-watermark.
         // will not assume id != LOG_ID_EVENTS or LOG_ID_SECURITY for KISS and
         // long term code stability, find() check should be fast for those ids.
-        LogBufferPidIteratorMap::iterator found =
-            mLastWorstPidOfSystem[id].find(element->getPid());
-        if ((found != mLastWorstPidOfSystem[id].end()) &&
-            (it == found->second)) {
+        LogBufferPidIteratorMap::iterator found = mLastWorstPidOfSystem[id].find(element->getPid());
+        if (found != mLastWorstPidOfSystem[id].end() && it == found->second) {
             mLastWorstPidOfSystem[id].erase(found);
         }
     }
@@ -400,9 +383,8 @@
     log_id_for_each(i) { doSetLast |= setLast[i] = oldest_[i] && it == *oldest_[i]; }
 #ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
     LogBufferElementCollection::iterator bad = it;
-    int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
-                  ? element->getTag()
-                  : element->getUid();
+    int key =
+            (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element->getTag() : element->getUid();
 #endif
     it = mLogElements.erase(it);
     if (doSetLast) {
@@ -421,14 +403,12 @@
     log_id_for_each(i) {
         for (auto b : mLastWorst[i]) {
             if (bad == b.second) {
-                android::prdebug("stale mLastWorst[%d] key=%d mykey=%d\n", i,
-                                 b.first, key);
+                android::prdebug("stale mLastWorst[%d] key=%d mykey=%d\n", i, b.first, key);
             }
         }
         for (auto b : mLastWorstPidOfSystem[i]) {
             if (bad == b.second) {
-                android::prdebug("stale mLastWorstPidOfSystem[%d] pid=%d\n", i,
-                                 b.first);
+                android::prdebug("stale mLastWorstPidOfSystem[%d] pid=%d\n", i, b.first);
             }
         }
     }
@@ -456,26 +436,20 @@
         uint64_t value;
     } __packed;
 
-   public:
-    LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid)
-        : uid(uid), pid(pid), tid(tid) {
-    }
-    explicit LogBufferElementKey(uint64_t key) : value(key) {
-    }
+  public:
+    LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid) : uid(uid), pid(pid), tid(tid) {}
+    explicit LogBufferElementKey(uint64_t key) : value(key) {}
 
-    uint64_t getKey() {
-        return value;
-    }
+    uint64_t getKey() { return value; }
 };
 
 class LogBufferElementLast {
     typedef std::unordered_map<uint64_t, LogBufferElement*> LogBufferElementMap;
     LogBufferElementMap map;
 
-   public:
+  public:
     bool coalesce(LogBufferElement* element, uint16_t dropped) {
-        LogBufferElementKey key(element->getUid(), element->getPid(),
-                                element->getTid());
+        LogBufferElementKey key(element->getUid(), element->getPid(), element->getTid());
         LogBufferElementMap::iterator it = map.find(key.getKey());
         if (it != map.end()) {
             LogBufferElement* found = it->second;
@@ -491,14 +465,11 @@
     }
 
     void add(LogBufferElement* element) {
-        LogBufferElementKey key(element->getUid(), element->getPid(),
-                                element->getTid());
+        LogBufferElementKey key(element->getUid(), element->getPid(), element->getTid());
         map[key.getKey()] = element;
     }
 
-    inline void clear() {
-        map.clear();
-    }
+    void clear() { map.clear(); }
 
     void clear(LogBufferElement* element) {
         uint64_t current = element->getRealTime().nsec() - (EXPIRE_RATELIMIT * NS_PER_SEC);
@@ -516,22 +487,21 @@
 
 // If the selected reader is blocking our pruning progress, decide on
 // what kind of mitigation is necessary to unblock the situation.
-void LogBuffer::kickMe(LogReaderThread* me, log_id_t id, unsigned long pruneRows) {
+void ChattyLogBuffer::kickMe(LogReaderThread* me, log_id_t id, unsigned long pruneRows) {
     if (stats_->Sizes(id) > (2 * log_buffer_size(id))) {  // +100%
         // A misbehaving or slow reader has its connection
         // dropped if we hit too much memory pressure.
-        android::prdebug("Kicking blocked reader, pid %d, from LogBuffer::kickMe()\n",
-                         me->client()->getPid());
+        android::prdebug("Kicking blocked reader, %s, from ChattyLogBuffer::kickMe()\n",
+                         me->name().c_str());
         me->release_Locked();
-    } else if (me->timeout().tv_sec || me->timeout().tv_nsec) {
-        // Allow a blocked WRAP timeout reader to
-        // trigger and start reporting the log data.
+    } else if (me->deadline().time_since_epoch().count() != 0) {
+        // Allow a blocked WRAP deadline reader to trigger and start reporting the log data.
         me->triggerReader_Locked();
     } else {
         // tell slow reader to skip entries to catch up
         android::prdebug(
-                "Skipping %lu entries from slow reader, pid %d, from LogBuffer::kickMe()\n",
-                pruneRows, me->client()->getPid());
+                "Skipping %lu entries from slow reader, %s, from ChattyLogBuffer::kickMe()\n",
+                pruneRows, me->name().c_str());
         me->triggerSkip_Locked(id, pruneRows);
     }
 }
@@ -581,25 +551,25 @@
 // The third thread is optional, and only gets hit if there was a whitelist
 // and more needs to be pruned against the backstop of the region lock.
 //
-// LogBuffer::wrlock() must be held when this function is called.
+// ChattyLogBuffer::wrlock() must be held when this function is called.
 //
-bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
+bool ChattyLogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
     LogReaderThread* oldest = nullptr;
     bool busy = false;
     bool clearAll = pruneRows == ULONG_MAX;
 
-    LogReaderThread::rdlock();
+    auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
 
     // Region locked?
-    LastLogTimes::iterator times = mTimes.begin();
-    while (times != mTimes.end()) {
-        LogReaderThread* entry = times->get();
-        if (entry->IsWatching(id) && (!oldest || oldest->start() > entry->start() ||
-                                      (oldest->start() == entry->start() &&
-                                       (entry->timeout().tv_sec || entry->timeout().tv_nsec)))) {
-            oldest = entry;
+    for (const auto& reader_thread : reader_list_->reader_threads()) {
+        if (!reader_thread->IsWatching(id)) {
+            continue;
         }
-        times++;
+        if (!oldest || oldest->start() > reader_thread->start() ||
+            (oldest->start() == reader_thread->start() &&
+             reader_thread->deadline().time_since_epoch().count() != 0)) {
+            oldest = reader_thread.get();
+        }
     }
 
     LogBufferElementCollection::iterator it;
@@ -611,8 +581,7 @@
         while (it != mLogElements.end()) {
             LogBufferElement* element = *it;
 
-            if ((element->getLogId() != id) ||
-                (element->getUid() != caller_uid)) {
+            if (element->getLogId() != id || element->getUid() != caller_uid) {
                 ++it;
                 continue;
             }
@@ -628,7 +597,6 @@
                 break;
             }
         }
-        LogReaderThread::unlock();
         return busy;
     }
 
@@ -672,10 +640,8 @@
         bool gc = pruneRows <= 1;
         if (!gc && (worst != -1)) {
             {  // begin scope for worst found iterator
-                LogBufferIteratorMap::iterator found =
-                    mLastWorst[id].find(worst);
-                if ((found != mLastWorst[id].end()) &&
-                    (found->second != mLogElements.end())) {
+                LogBufferIteratorMap::iterator found = mLastWorst[id].find(worst);
+                if (found != mLastWorst[id].end() && found->second != mLogElements.end()) {
                     leading = false;
                     it = found->second;
                 }
@@ -683,10 +649,9 @@
             if (worstPid) {  // begin scope for pid worst found iterator
                 // FYI: worstPid only set if !LOG_ID_EVENTS and
                 //      !LOG_ID_SECURITY, not going to make that assumption ...
-                LogBufferPidIteratorMap::iterator found =
-                    mLastWorstPidOfSystem[id].find(worstPid);
-                if ((found != mLastWorstPidOfSystem[id].end()) &&
-                    (found->second != mLogElements.end())) {
+                LogBufferPidIteratorMap::iterator found = mLastWorstPidOfSystem[id].find(worstPid);
+                if (found != mLastWorstPidOfSystem[id].end() &&
+                    found->second != mLogElements.end()) {
                     leading = false;
                     it = found->second;
                 }
@@ -695,7 +660,7 @@
         if (leading) {
             it = GetOldest(id);
         }
-        static const timespec too_old = { EXPIRE_HOUR_THRESHOLD * 60 * 60, 0 };
+        static const timespec too_old = {EXPIRE_HOUR_THRESHOLD * 60 * 60, 0};
         LogBufferElementCollection::iterator lastt;
         lastt = mLogElements.end();
         --lastt;
@@ -728,9 +693,8 @@
                 continue;
             }
 
-            int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
-                          ? element->getTag()
-                          : element->getUid();
+            int key = (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element->getTag()
+                                                                     : element->getUid();
 
             if (hasBlacklist && prune_->naughty(element)) {
                 last.clear(element);
@@ -761,10 +725,9 @@
 
             if (dropped) {
                 last.add(element);
-                if (worstPid &&
-                    ((!gc && (element->getPid() == worstPid)) ||
-                     (mLastWorstPidOfSystem[id].find(element->getPid()) ==
-                      mLastWorstPidOfSystem[id].end()))) {
+                if (worstPid && ((!gc && element->getPid() == worstPid) ||
+                                 mLastWorstPidOfSystem[id].find(element->getPid()) ==
+                                         mLastWorstPidOfSystem[id].end())) {
                     // element->getUid() may not be AID_SYSTEM, next best
                     // watermark if current one empty. id is not LOG_ID_EVENTS
                     // or LOG_ID_SECURITY because of worstPid check.
@@ -778,8 +741,7 @@
                 continue;
             }
 
-            if ((key != worst) ||
-                (worstPid && (element->getPid() != worstPid))) {
+            if (key != worst || (worstPid && element->getPid() != worstPid)) {
                 leading = false;
                 last.clear(element);
                 ++it;
@@ -807,16 +769,14 @@
                     it = erase(it, true);
                 } else {
                     last.add(element);
-                    if (worstPid &&
-                        (!gc || (mLastWorstPidOfSystem[id].find(worstPid) ==
-                                 mLastWorstPidOfSystem[id].end()))) {
+                    if (worstPid && (!gc || mLastWorstPidOfSystem[id].find(worstPid) ==
+                                                    mLastWorstPidOfSystem[id].end())) {
                         // element->getUid() may not be AID_SYSTEM, next best
                         // watermark if current one empty. id is not
                         // LOG_ID_EVENTS or LOG_ID_SECURITY because of worstPid.
                         mLastWorstPidOfSystem[id][worstPid] = it;
                     }
-                    if ((!gc && !worstPid) ||
-                        (mLastWorst[id].find(worst) == mLastWorst[id].end())) {
+                    if ((!gc && !worstPid) || mLastWorst[id].find(worst) == mLastWorst[id].end()) {
                         mLastWorst[id][worst] = it;
                     }
                     ++it;
@@ -884,13 +844,11 @@
         }
     }
 
-    LogReaderThread::unlock();
-
     return (pruneRows > 0) && busy;
 }
 
 // clear all rows of type "id" from the buffer.
-bool LogBuffer::clear(log_id_t id, uid_t uid) {
+bool ChattyLogBuffer::Clear(log_id_t id, uid_t uid) {
     bool busy = true;
     // If it takes more than 4 tries (seconds) to clear, then kill reader(s)
     for (int retry = 4;;) {
@@ -907,20 +865,15 @@
             // readers and let the clear run (below) deal with determining
             // if we are still blocked and return an error code to caller.
             if (busy) {
-                LogReaderThread::wrlock();
-                LastLogTimes::iterator times = mTimes.begin();
-                while (times != mTimes.end()) {
-                    LogReaderThread* entry = times->get();
-                    // Killer punch
-                    if (entry->IsWatching(id)) {
+                auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
+                for (const auto& reader_thread : reader_list_->reader_threads()) {
+                    if (reader_thread->IsWatching(id)) {
                         android::prdebug(
-                                "Kicking blocked reader, pid %d, from LogBuffer::clear()\n",
-                                entry->client()->getPid());
-                        entry->release_Locked();
+                                "Kicking blocked reader, %s, from ChattyLogBuffer::clear()\n",
+                                reader_thread->name().c_str());
+                        reader_thread->release_Locked();
                     }
-                    times++;
                 }
-                LogReaderThread::unlock();
             }
         }
         wrlock();
@@ -935,7 +888,7 @@
 }
 
 // set the total space allocated to "id"
-int LogBuffer::setSize(log_id_t id, unsigned long size) {
+int ChattyLogBuffer::SetSize(log_id_t id, unsigned long size) {
     // Reasonable limits ...
     if (!__android_logger_valid_buffer_size(size)) {
         return -1;
@@ -947,18 +900,18 @@
 }
 
 // get the total space allocated to "id"
-unsigned long LogBuffer::getSize(log_id_t id) {
+unsigned long ChattyLogBuffer::GetSize(log_id_t id) {
     rdlock();
     size_t retval = log_buffer_size(id);
     unlock();
     return retval;
 }
 
-uint64_t LogBuffer::flushTo(SocketClient* reader, uint64_t start, pid_t* lastTid, bool privileged,
-                            bool security,
-                            const std::function<int(const LogBufferElement* element)>& filter) {
+uint64_t ChattyLogBuffer::FlushTo(
+        LogWriter* writer, uint64_t start, pid_t* lastTid,
+        const std::function<FlushToResult(const LogBufferElement* element)>& filter) {
     LogBufferElementCollection::iterator it;
-    uid_t uid = reader->getUid();
+    uid_t uid = writer->uid();
 
     rdlock();
 
@@ -984,21 +937,21 @@
     for (; it != mLogElements.end(); ++it) {
         LogBufferElement* element = *it;
 
-        if (!privileged && (element->getUid() != uid)) {
+        if (!writer->privileged() && element->getUid() != uid) {
             continue;
         }
 
-        if (!security && (element->getLogId() == LOG_ID_SECURITY)) {
+        if (!writer->can_read_security_logs() && element->getLogId() == LOG_ID_SECURITY) {
             continue;
         }
 
         // NB: calling out to another object with wrlock() held (safe)
         if (filter) {
-            int ret = filter(element);
-            if (ret == false) {
+            FlushToResult ret = filter(element);
+            if (ret == FlushToResult::kSkip) {
                 continue;
             }
-            if (ret != true) {
+            if (ret == FlushToResult::kStop) {
                 break;
             }
         }
@@ -1012,16 +965,15 @@
             // is due to spam filter.  chatty to chatty of different
             // source is also due to spam filter.
             lastTid[element->getLogId()] =
-                (element->getDropped() && !sameTid) ? 0 : element->getTid();
+                    (element->getDropped() && !sameTid) ? 0 : element->getTid();
         }
 
         unlock();
 
+        curr = element->getSequence();
         // range locking in LastLogTimes looks after us
-        curr = element->flushTo(reader, stats_, sameTid);
-
-        if (curr == element->FLUSH_ERROR) {
-            return curr;
+        if (!element->FlushTo(writer, stats_, sameTid)) {
+            return FLUSH_ERROR;
         }
 
         rdlock();
diff --git a/logd/ChattyLogBuffer.h b/logd/ChattyLogBuffer.h
new file mode 100644
index 0000000..29a421d
--- /dev/null
+++ b/logd/ChattyLogBuffer.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <list>
+#include <optional>
+#include <string>
+
+#include <android/log.h>
+#include <private/android_filesystem_config.h>
+#include <sysutils/SocketClient.h>
+
+#include "LogBuffer.h"
+#include "LogBufferElement.h"
+#include "LogReaderList.h"
+#include "LogReaderThread.h"
+#include "LogStatistics.h"
+#include "LogTags.h"
+#include "LogWhiteBlackList.h"
+#include "LogWriter.h"
+
+typedef std::list<LogBufferElement*> LogBufferElementCollection;
+
+class ChattyLogBuffer : public LogBuffer {
+    LogBufferElementCollection mLogElements;
+    pthread_rwlock_t mLogElementsLock;
+
+    // watermark of any worst/chatty uid processing
+    typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator> LogBufferIteratorMap;
+    LogBufferIteratorMap mLastWorst[LOG_ID_MAX];
+    // watermark of any worst/chatty pid of system processing
+    typedef std::unordered_map<pid_t, LogBufferElementCollection::iterator> LogBufferPidIteratorMap;
+    LogBufferPidIteratorMap mLastWorstPidOfSystem[LOG_ID_MAX];
+
+    unsigned long mMaxSize[LOG_ID_MAX];
+
+    LogBufferElement* lastLoggedElements[LOG_ID_MAX];
+    LogBufferElement* droppedElements[LOG_ID_MAX];
+    void log(LogBufferElement* elem);
+
+  public:
+    ChattyLogBuffer(LogReaderList* reader_list, LogTags* tags, PruneList* prune,
+                    LogStatistics* stats);
+    ~ChattyLogBuffer();
+    void Init() override;
+
+    int Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg,
+            uint16_t len) override;
+    uint64_t FlushTo(
+            LogWriter* writer, uint64_t start, pid_t* lastTid,
+            const std::function<FlushToResult(const LogBufferElement* element)>& filter) override;
+
+    bool Clear(log_id_t id, uid_t uid = AID_ROOT) override;
+    unsigned long GetSize(log_id_t id) override;
+    int SetSize(log_id_t id, unsigned long size) override;
+
+  private:
+    void wrlock() { pthread_rwlock_wrlock(&mLogElementsLock); }
+    void rdlock() { pthread_rwlock_rdlock(&mLogElementsLock); }
+    void unlock() { pthread_rwlock_unlock(&mLogElementsLock); }
+
+    void maybePrune(log_id_t id);
+    void kickMe(LogReaderThread* me, log_id_t id, unsigned long pruneRows);
+
+    bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
+    LogBufferElementCollection::iterator erase(LogBufferElementCollection::iterator it,
+                                               bool coalesce = false);
+
+    // Returns an iterator to the oldest element for a given log type, or mLogElements.end() if
+    // there are no logs for the given log type. Requires mLogElementsLock to be held.
+    LogBufferElementCollection::iterator GetOldest(log_id_t log_id);
+
+    LogReaderList* reader_list_;
+    LogTags* tags_;
+    PruneList* prune_;
+    LogStatistics* stats_;
+
+    // Keeps track of the iterator to the oldest log message of a given log type, as an
+    // optimization when pruning logs.  Use GetOldest() to retrieve.
+    std::optional<LogBufferElementCollection::iterator> oldest_[LOG_ID_MAX];
+};
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 87402ac..c6ab22d 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -37,7 +37,7 @@
 #include <private/android_filesystem_config.h>
 #include <sysutils/SocketClient.h>
 
-#include "LogUtils.h"
+#include "LogPermissions.h"
 
 CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune,
                                  LogStatistics* stats)
@@ -81,7 +81,7 @@
         return 0;
     }
 
-    cli->sendMsg(buf()->clear((log_id_t)id, uid) ? "busy" : "success");
+    cli->sendMsg(buf()->Clear((log_id_t)id, uid) ? "busy" : "success");
     return 0;
 }
 
@@ -99,7 +99,7 @@
         return 0;
     }
 
-    unsigned long size = buf()->getSize((log_id_t)id);
+    unsigned long size = buf()->GetSize((log_id_t)id);
     char buf[512];
     snprintf(buf, sizeof(buf), "%lu", size);
     cli->sendMsg(buf);
@@ -126,7 +126,7 @@
     }
 
     unsigned long size = atol(argv[2]);
-    if (buf()->setSize((log_id_t)id, size)) {
+    if (buf()->SetSize((log_id_t)id, size)) {
         cli->sendMsg("Range Error");
         return 0;
     }
@@ -299,7 +299,7 @@
     setname();
 
     android::prdebug("logd reinit");
-    buf()->init();
+    buf()->Init();
     prune()->init(nullptr);
 
     // This only works on userdebug and eng devices to re-read the
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index fd934f7..a55a393 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -16,12 +16,11 @@
 
 #pragma once
 
+#include <sysutils/FrameworkCommand.h>
 #include <sysutils/FrameworkListener.h>
 
 #include "LogBuffer.h"
-#include "LogCommand.h"
 #include "LogListener.h"
-#include "LogReader.h"
 #include "LogStatistics.h"
 #include "LogTags.h"
 #include "LogWhiteBlackList.h"
@@ -39,20 +38,20 @@
     PruneList* prune_;
     LogStatistics* stats_;
 
-#define LogCmd(name, command_string)                             \
-    class name##Cmd : public LogCommand {                        \
-      public:                                                    \
-        explicit name##Cmd(CommandListener* parent)              \
-            : LogCommand(#command_string), parent_(parent) {}    \
-        virtual ~name##Cmd() {}                                  \
-        int runCommand(SocketClient* c, int argc, char** argv);  \
-                                                                 \
-      private:                                                   \
-        LogBuffer* buf() const { return parent_->buf_; }         \
-        LogTags* tags() const { return parent_->tags_; }         \
-        PruneList* prune() const { return parent_->prune_; }     \
-        LogStatistics* stats() const { return parent_->stats_; } \
-        CommandListener* parent_;                                \
+#define LogCmd(name, command_string)                                \
+    class name##Cmd : public FrameworkCommand {                     \
+      public:                                                       \
+        explicit name##Cmd(CommandListener* parent)                 \
+            : FrameworkCommand(#command_string), parent_(parent) {} \
+        virtual ~name##Cmd() {}                                     \
+        int runCommand(SocketClient* c, int argc, char** argv);     \
+                                                                    \
+      private:                                                      \
+        LogBuffer* buf() const { return parent_->buf_; }            \
+        LogTags* tags() const { return parent_->tags_; }            \
+        PruneList* prune() const { return parent_->prune_; }        \
+        LogStatistics* stats() const { return parent_->stats_; }    \
+        CommandListener* parent_;                                   \
     }
 
     LogCmd(Clear, clear);
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 6c42a28..0ce9796 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -37,7 +37,6 @@
 #include <private/android_logger.h>
 
 #include "LogKlog.h"
-#include "LogReader.h"
 #include "LogUtils.h"
 #include "libaudit.h"
 
@@ -45,10 +44,9 @@
     '<', '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) / 10, \
         '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) % 10, '>'
 
-LogAudit::LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg, LogStatistics* stats)
+LogAudit::LogAudit(LogBuffer* buf, int fdDmesg, LogStatistics* stats)
     : SocketListener(getLogSocket(), false),
       logbuf(buf),
-      reader(reader),
       fdDmesg(fdDmesg),
       main(__android_logger_property_get_bool("ro.logd.auditd.main", BOOL_DEFAULT_TRUE)),
       events(__android_logger_property_get_bool("ro.logd.auditd.events", BOOL_DEFAULT_TRUE)),
@@ -276,9 +274,8 @@
         memcpy(event->data + str_len - denial_metadata.length(),
                denial_metadata.c_str(), denial_metadata.length());
 
-        rc = logbuf->log(
-            LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),
-            (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
+        rc = logbuf->Log(LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),
+                         (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
         if (rc >= 0) {
             notify |= 1 << LOG_ID_EVENTS;
         }
@@ -330,9 +327,8 @@
         strncpy(newstr + 1 + str_len + prefix_len + suffix_len,
                 denial_metadata.c_str(), denial_metadata.length());
 
-        rc = logbuf->log(
-            LOG_ID_MAIN, now, uid, pid, tid, newstr,
-            (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
+        rc = logbuf->Log(LOG_ID_MAIN, now, uid, pid, tid, newstr,
+                         (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
 
         if (rc >= 0) {
             notify |= 1 << LOG_ID_MAIN;
@@ -344,7 +340,6 @@
     free(str);
 
     if (notify) {
-        reader->notifyNewLog(notify);
         if (rc < 0) {
             rc = message_len;
         }
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index ee6e579..181920e 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -23,18 +23,15 @@
 #include "LogBuffer.h"
 #include "LogStatistics.h"
 
-class LogReader;
-
 class LogAudit : public SocketListener {
     LogBuffer* logbuf;
-    LogReader* reader;
     int fdDmesg;  // fdDmesg >= 0 is functionally bool dmesg
     bool main;
     bool events;
     bool initialized;
 
   public:
-    LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg, LogStatistics* stats);
+    LogAudit(LogBuffer* buf, int fdDmesg, LogStatistics* stats);
     int log(char* buf, size_t len);
 
   protected:
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 3c1ea5a..6274051 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2014 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.
@@ -18,89 +18,39 @@
 
 #include <sys/types.h>
 
-#include <list>
-#include <optional>
-#include <string>
+#include <functional>
 
-#include <android/log.h>
-#include <private/android_filesystem_config.h>
+#include <log/log.h>
 #include <sysutils/SocketClient.h>
 
 #include "LogBufferElement.h"
-#include "LogReaderThread.h"
-#include "LogStatistics.h"
-#include "LogTags.h"
-#include "LogWhiteBlackList.h"
 
-typedef std::list<LogBufferElement*> LogBufferElementCollection;
+class LogWriter;
+
+enum class FlushToResult {
+    kSkip,
+    kStop,
+    kWrite,
+};
 
 class LogBuffer {
-    LogBufferElementCollection mLogElements;
-    pthread_rwlock_t mLogElementsLock;
+  public:
+    virtual ~LogBuffer() {}
 
-    // watermark of any worst/chatty uid processing
-    typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator>
-        LogBufferIteratorMap;
-    LogBufferIteratorMap mLastWorst[LOG_ID_MAX];
-    // watermark of any worst/chatty pid of system processing
-    typedef std::unordered_map<pid_t, LogBufferElementCollection::iterator>
-        LogBufferPidIteratorMap;
-    LogBufferPidIteratorMap mLastWorstPidOfSystem[LOG_ID_MAX];
+    virtual void Init() = 0;
 
-    unsigned long mMaxSize[LOG_ID_MAX];
-
-    LogBufferElement* lastLoggedElements[LOG_ID_MAX];
-    LogBufferElement* droppedElements[LOG_ID_MAX];
-    void log(LogBufferElement* elem);
-
-   public:
-    LastLogTimes& mTimes;
-
-    LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune, LogStatistics* stats);
-    ~LogBuffer();
-    void init();
-
-    int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg,
-            uint16_t len);
+    virtual int Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
+                    const char* msg, uint16_t len) = 0;
     // lastTid is an optional context to help detect if the last previous
     // valid message was from the same source so we can differentiate chatty
     // filter types (identical or expired)
-    uint64_t flushTo(SocketClient* writer, uint64_t start,
-                     pid_t* lastTid,  // &lastTid[LOG_ID_MAX] or nullptr
-                     bool privileged, bool security,
-                     const std::function<int(const LogBufferElement* element)>& filter);
+    static const uint64_t FLUSH_ERROR = 0;
+    virtual uint64_t FlushTo(
+            LogWriter* writer, uint64_t start,
+            pid_t* last_tid,  // nullable
+            const std::function<FlushToResult(const LogBufferElement* element)>& filter) = 0;
 
-    bool clear(log_id_t id, uid_t uid = AID_ROOT);
-    unsigned long getSize(log_id_t id);
-    int setSize(log_id_t id, unsigned long size);
-
-  private:
-    void wrlock() {
-        pthread_rwlock_wrlock(&mLogElementsLock);
-    }
-    void rdlock() {
-        pthread_rwlock_rdlock(&mLogElementsLock);
-    }
-    void unlock() {
-        pthread_rwlock_unlock(&mLogElementsLock);
-    }
-
-    void maybePrune(log_id_t id);
-    void kickMe(LogReaderThread* me, log_id_t id, unsigned long pruneRows);
-
-    bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
-    LogBufferElementCollection::iterator erase(
-        LogBufferElementCollection::iterator it, bool coalesce = false);
-
-    // Returns an iterator to the oldest element for a given log type, or mLogElements.end() if
-    // there are no logs for the given log type. Requires mLogElementsLock to be held.
-    LogBufferElementCollection::iterator GetOldest(log_id_t log_id);
-
-    LogTags* tags_;
-    PruneList* prune_;
-    LogStatistics* stats_;
-
-    // Keeps track of the iterator to the oldest log message of a given log type, as an
-    // optimization when pruning logs.  Use GetOldest() to retrieve.
-    std::optional<LogBufferElementCollection::iterator> oldest_[LOG_ID_MAX];
-};
+    virtual bool Clear(log_id_t id, uid_t uid) = 0;
+    virtual unsigned long GetSize(log_id_t id) = 0;
+    virtual int SetSize(log_id_t id, unsigned long size) = 0;
+};
\ No newline at end of file
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index cc68ba4..8499715 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -27,12 +27,9 @@
 #include <log/log_read.h>
 #include <private/android_logger.h>
 
-#include "LogBuffer.h"
-#include "LogCommand.h"
-#include "LogReader.h"
+#include "LogStatistics.h"
 #include "LogUtils.h"
 
-const uint64_t LogBufferElement::FLUSH_ERROR(0);
 atomic_int_fast64_t LogBufferElement::sequence(1);
 
 LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
@@ -243,7 +240,7 @@
     return retval;
 }
 
-uint64_t LogBufferElement::flushTo(SocketClient* reader, LogStatistics* stats, bool lastSame) {
+bool LogBufferElement::FlushTo(LogWriter* writer, LogStatistics* stats, bool lastSame) {
     struct logger_entry entry = {};
 
     entry.hdr_size = sizeof(struct logger_entry);
@@ -254,23 +251,18 @@
     entry.sec = mRealTime.tv_sec;
     entry.nsec = mRealTime.tv_nsec;
 
-    struct iovec iovec[2];
-    iovec[0].iov_base = &entry;
-    iovec[0].iov_len = entry.hdr_size;
-
     char* buffer = nullptr;
-
+    const char* msg;
     if (mDropped) {
         entry.len = populateDroppedMessage(buffer, stats, lastSame);
-        if (!entry.len) return mSequence;
-        iovec[1].iov_base = buffer;
+        if (!entry.len) return true;
+        msg = buffer;
     } else {
+        msg = mMsg;
         entry.len = mMsgLen;
-        iovec[1].iov_base = mMsg;
     }
-    iovec[1].iov_len = entry.len;
 
-    uint64_t retval = reader->sendDatav(iovec, 1 + (entry.len != 0)) ? FLUSH_ERROR : mSequence;
+    bool retval = writer->Write(entry, msg);
 
     if (buffer) free(buffer);
 
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 8676cf1..2f2d70d 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -22,9 +22,9 @@
 #include <sys/types.h>
 
 #include <log/log.h>
-#include <sysutils/SocketClient.h>
 
-class LogBuffer;
+#include "LogWriter.h"
+
 class LogStatistics;
 
 #define EXPIRE_HOUR_THRESHOLD 24  // Only expire chatty UID logs to preserve
@@ -34,8 +34,6 @@
 #define EXPIRE_RATELIMIT 10  // maximum rate in seconds to report expiration
 
 class __attribute__((packed)) LogBufferElement {
-    friend LogBuffer;
-
     // sized to match reality of incoming log packets
     const uint32_t mUid;
     const uint32_t mPid;
@@ -97,6 +95,5 @@
         return mRealTime;
     }
 
-    static const uint64_t FLUSH_ERROR;
-    uint64_t flushTo(SocketClient* writer, LogStatistics* parent, bool lastSame);
+    bool FlushTo(LogWriter* writer, LogStatistics* parent, bool lastSame);
 };
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index bc94b45..1ea87a9 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -31,7 +31,6 @@
 #include <private/android_logger.h>
 
 #include "LogBuffer.h"
-#include "LogReader.h"
 
 #define KMSG_PRIORITY(PRI) \
     '<', '0' + (LOG_SYSLOG | (PRI)) / 10, '0' + (LOG_SYSLOG | (PRI)) % 10, '>'
@@ -202,11 +201,9 @@
                                        ? log_time(log_time::EPOCH)
                                        : (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
 
-LogKlog::LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead, bool auditd,
-                 LogStatistics* stats)
+LogKlog::LogKlog(LogBuffer* buf, int fdWrite, int fdRead, bool auditd, LogStatistics* stats)
     : SocketListener(fdRead, false),
       logbuf(buf),
-      reader(reader),
       signature(CLOCK_MONOTONIC),
       initialized(false),
       enableLogging(true),
@@ -770,12 +767,7 @@
     }
 
     // Log message
-    int rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, (uint16_t)n);
-
-    // notify readers
-    if (rc > 0) {
-        reader->notifyNewLog(static_cast<unsigned int>(1 << LOG_ID_KERNEL));
-    }
+    int rc = logbuf->Log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, (uint16_t)n);
 
     return rc;
 }
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index a7dbe64..56e0452 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -19,14 +19,11 @@
 #include <private/android_logger.h>
 #include <sysutils/SocketListener.h>
 
+#include "LogBuffer.h"
 #include "LogStatistics.h"
 
-class LogBuffer;
-class LogReader;
-
 class LogKlog : public SocketListener {
     LogBuffer* logbuf;
-    LogReader* reader;
     const log_time signature;
     // Set once thread is started, separates KLOG_ACTION_READ_ALL
     // and KLOG_ACTION_READ phases.
@@ -40,8 +37,7 @@
     static log_time correction;
 
   public:
-    LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead, bool auditd,
-            LogStatistics* stats);
+    LogKlog(LogBuffer* buf, int fdWrite, int fdRead, bool auditd, LogStatistics* stats);
     int log(const char* buf, ssize_t len);
 
     static void convertMonotonicToReal(log_time& real) { real += correction; }
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index fbe6ea0..a6ab50b 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -30,10 +30,9 @@
 
 #include "LogBuffer.h"
 #include "LogListener.h"
-#include "LogUtils.h"
+#include "LogPermissions.h"
 
-LogListener::LogListener(LogBuffer* buf, LogReader* reader)
-    : socket_(GetLogSocket()), logbuf_(buf), reader_(reader) {}
+LogListener::LogListener(LogBuffer* buf) : socket_(GetLogSocket()), logbuf_(buf) {}
 
 bool LogListener::StartListener() {
     if (socket_ <= 0) {
@@ -45,11 +44,7 @@
 }
 
 void LogListener::ThreadFunction() {
-    static bool name_set;
-    if (!name_set) {
-        prctl(PR_SET_NAME, "logd.writer");
-        name_set = true;
-    }
+    prctl(PR_SET_NAME, "logd.writer");
 
     while (true) {
         HandleData();
@@ -121,13 +116,8 @@
     // NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
     // truncated message to the logs.
 
-    int res = logbuf_->log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
-                           ((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
-    if (res > 0) {
-        reader_->notifyNewLog(static_cast<unsigned int>(1 << logId));
-    }
-
-    return;
+    logbuf_->Log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
+                 ((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
 }
 
 int LogListener::GetLogSocket() {
diff --git a/logd/LogListener.h b/logd/LogListener.h
index ce3e0f2..c114e38 100644
--- a/logd/LogListener.h
+++ b/logd/LogListener.h
@@ -17,11 +17,10 @@
 #pragma once
 
 #include "LogBuffer.h"
-#include "LogReader.h"
 
 class LogListener {
   public:
-    LogListener(LogBuffer* buf, LogReader* reader);
+    LogListener(LogBuffer* buf);
     bool StartListener();
 
   private:
@@ -31,5 +30,4 @@
 
     int socket_;
     LogBuffer* logbuf_;
-    LogReader* reader_;
 };
diff --git a/logd/LogCommand.cpp b/logd/LogPermissions.cpp
similarity index 97%
rename from logd/LogCommand.cpp
rename to logd/LogPermissions.cpp
index 8bff9da..8f02d5a 100644
--- a/logd/LogCommand.cpp
+++ b/logd/LogPermissions.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "LogPermissions.h"
+
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -21,12 +23,6 @@
 
 #include <private/android_filesystem_config.h>
 
-#include "LogCommand.h"
-#include "LogUtils.h"
-
-LogCommand::LogCommand(const char* cmd) : FrameworkCommand(cmd) {
-}
-
 // gets a list of supplementary group IDs associated with
 // the socket peer.  This is implemented by opening
 // /proc/PID/status and look for the "Group:" line.
diff --git a/logd/LogCommand.h b/logd/LogPermissions.h
similarity index 74%
rename from logd/LogCommand.h
rename to logd/LogPermissions.h
index e10ffa0..3130db5 100644
--- a/logd/LogCommand.h
+++ b/logd/LogPermissions.h
@@ -14,17 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef _LOGD_COMMAND_H
-#define _LOGD_COMMAND_H
+#pragma once
 
-#include <sysutils/FrameworkCommand.h>
+#include <sys/types.h>
+
 #include <sysutils/SocketClient.h>
 
-class LogCommand : public FrameworkCommand {
-   public:
-    explicit LogCommand(const char* cmd);
-    virtual ~LogCommand() {
-    }
-};
-
-#endif
+bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid);
+bool clientHasLogCredentials(SocketClient* cli);
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index a590cef..89562a4 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -21,39 +21,62 @@
 #include <sys/socket.h>
 #include <sys/types.h>
 
+#include <chrono>
+
+#include <android-base/stringprintf.h>
 #include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
 #include "LogBuffer.h"
 #include "LogBufferElement.h"
+#include "LogPermissions.h"
 #include "LogReader.h"
 #include "LogUtils.h"
+#include "LogWriter.h"
 
 static bool CanReadSecurityLogs(SocketClient* client) {
     return client->getUid() == AID_SYSTEM || client->getGid() == AID_SYSTEM;
 }
 
-LogReader::LogReader(LogBuffer* logbuf)
-    : SocketListener(getLogSocket(), true), mLogbuf(*logbuf) {
+static std::string SocketClientToName(SocketClient* client) {
+    return android::base::StringPrintf("pid %d, fd %d", client->getPid(), client->getSocket());
 }
 
-// When we are notified a new log entry is available, inform
-// listening sockets who are watching this entry's log id.
-void LogReader::notifyNewLog(unsigned int log_mask) {
-    LastLogTimes& times = mLogbuf.mTimes;
+class SocketLogWriter : public LogWriter {
+  public:
+    SocketLogWriter(LogReader* reader, SocketClient* client, bool privileged,
+                    bool can_read_security_logs)
+        : LogWriter(client->getUid(), privileged, can_read_security_logs),
+          reader_(reader),
+          client_(client) {}
 
-    LogReaderThread::wrlock();
-    for (const auto& entry : times) {
-        if (!entry->IsWatchingMultiple(log_mask)) {
-            continue;
-        }
-        if (entry->timeout().tv_sec || entry->timeout().tv_nsec) {
-            continue;
-        }
-        entry->triggerReader_Locked();
+    bool Write(const logger_entry& entry, const char* msg) override {
+        struct iovec iovec[2];
+        iovec[0].iov_base = const_cast<logger_entry*>(&entry);
+        iovec[0].iov_len = entry.hdr_size;
+        iovec[1].iov_base = const_cast<char*>(msg);
+        iovec[1].iov_len = entry.len;
+
+        return client_->sendDatav(iovec, 1 + (entry.len != 0)) == 0;
     }
-    LogReaderThread::unlock();
-}
+
+    void Release() override {
+        reader_->release(client_);
+        client_->decRef();
+    }
+
+    void Shutdown() override { shutdown(client_->getSocket(), SHUT_RDWR); }
+
+    std::string name() const override { return SocketClientToName(client_); }
+
+  private:
+    LogReader* reader_;
+    SocketClient* client_;
+};
+
+LogReader::LogReader(LogBuffer* logbuf, LogReaderList* reader_list)
+    : SocketListener(getLogSocket(), true), log_buffer_(logbuf), reader_list_(reader_list) {}
 
 // Note returning false will release the SocketClient instance.
 bool LogReader::onDataAvailable(SocketClient* cli) {
@@ -67,22 +90,15 @@
 
     int len = read(cli->getSocket(), buffer, sizeof(buffer) - 1);
     if (len <= 0) {
-        doSocketDelete(cli);
+        DoSocketDelete(cli);
         return false;
     }
     buffer[len] = '\0';
 
-    // Clients are only allowed to send one command, disconnect them if they
-    // send another.
-    LogReaderThread::wrlock();
-    for (const auto& entry : mLogbuf.mTimes) {
-        if (entry->client() == cli) {
-            entry->release_Locked();
-            LogReaderThread::unlock();
-            return false;
-        }
+    // Clients are only allowed to send one command, disconnect them if they send another.
+    if (DoSocketDelete(cli)) {
+        return false;
     }
-    LogReaderThread::unlock();
 
     unsigned long tail = 0;
     static const char _tail[] = " tail=";
@@ -99,11 +115,12 @@
         start.strptime(cp + sizeof(_start) - 1, "%s.%q");
     }
 
-    uint64_t timeout = 0;
+    std::chrono::steady_clock::time_point deadline = {};
     static const char _timeout[] = " timeout=";
     cp = strstr(buffer, _timeout);
     if (cp) {
-        timeout = atol(cp + sizeof(_timeout) - 1) * NS_PER_SEC + log_time(CLOCK_MONOTONIC).nsec();
+        long timeout_seconds = atol(cp + sizeof(_timeout) - 1);
+        deadline = std::chrono::steady_clock::now() + std::chrono::seconds(timeout_seconds);
     }
 
     unsigned int logMask = -1;
@@ -137,8 +154,8 @@
     if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) {
         // Allow writer to get some cycles, and wait for pending notifications
         sched_yield();
-        LogReaderThread::wrlock();
-        LogReaderThread::unlock();
+        reader_list_->reader_threads_lock().lock();
+        reader_list_->reader_threads_lock().unlock();
         sched_yield();
         nonBlock = true;
     }
@@ -146,39 +163,41 @@
     bool privileged = clientHasLogCredentials(cli);
     bool can_read_security = CanReadSecurityLogs(cli);
 
+    std::unique_ptr<LogWriter> socket_log_writer(
+            new SocketLogWriter(this, cli, privileged, can_read_security));
+
     uint64_t sequence = 1;
     // Convert realtime to sequence number
     if (start != log_time::EPOCH) {
         bool start_time_set = false;
         uint64_t last = sequence;
         auto log_find_start = [pid, logMask, start, &sequence, &start_time_set,
-                               &last](const LogBufferElement* element) -> int {
+                               &last](const LogBufferElement* element) -> FlushToResult {
             if (pid && pid != element->getPid()) {
-                return 0;
+                return FlushToResult::kSkip;
             }
             if ((logMask & (1 << element->getLogId())) == 0) {
-                return 0;
+                return FlushToResult::kSkip;
             }
             if (start == element->getRealTime()) {
                 sequence = element->getSequence();
                 start_time_set = true;
-                return -1;
+                return FlushToResult::kStop;
             } else {
                 if (start < element->getRealTime()) {
                     sequence = last;
                     start_time_set = true;
-                    return -1;
+                    return FlushToResult::kStop;
                 }
                 last = element->getSequence();
             }
-            return 0;
+            return FlushToResult::kSkip;
         };
 
-        logbuf().flushTo(cli, sequence, nullptr, privileged, can_read_security, log_find_start);
+        log_buffer_->FlushTo(socket_log_writer.get(), sequence, nullptr, log_find_start);
 
         if (!start_time_set) {
             if (nonBlock) {
-                doSocketDelete(cli);
                 return false;
             }
             sequence = LogBufferElement::getCurrentSequence();
@@ -187,50 +206,40 @@
 
     android::prdebug(
             "logdr: UID=%d GID=%d PID=%d %c tail=%lu logMask=%x pid=%d "
-            "start=%" PRIu64 "ns timeout=%" PRIu64 "ns\n",
+            "start=%" PRIu64 "ns deadline=%" PRIi64 "ns\n",
             cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail, logMask,
-            (int)pid, start.nsec(), timeout);
+            (int)pid, start.nsec(), static_cast<int64_t>(deadline.time_since_epoch().count()));
 
     if (start == log_time::EPOCH) {
-        timeout = 0;
+        deadline = {};
     }
 
-    LogReaderThread::wrlock();
-    auto entry =
-            std::make_unique<LogReaderThread>(*this, cli, nonBlock, tail, logMask, pid, start,
-                                              sequence, timeout, privileged, can_read_security);
-    if (!entry->startReader_Locked()) {
-        LogReaderThread::unlock();
-        return false;
-    }
-
+    auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
+    auto entry = std::make_unique<LogReaderThread>(log_buffer_, reader_list_,
+                                                   std::move(socket_log_writer), nonBlock, tail,
+                                                   logMask, pid, start, sequence, deadline);
     // release client and entry reference counts once done
     cli->incRef();
-    mLogbuf.mTimes.emplace_front(std::move(entry));
+    reader_list_->reader_threads().emplace_front(std::move(entry));
 
     // Set acceptable upper limit to wait for slow reader processing b/27242723
     struct timeval t = { LOGD_SNDTIMEO, 0 };
     setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char*)&t,
                sizeof(t));
 
-    LogReaderThread::unlock();
-
     return true;
 }
 
-void LogReader::doSocketDelete(SocketClient* cli) {
-    LastLogTimes& times = mLogbuf.mTimes;
-    LogReaderThread::wrlock();
-    LastLogTimes::iterator it = times.begin();
-    while (it != times.end()) {
-        LogReaderThread* entry = it->get();
-        if (entry->client() == cli) {
-            entry->release_Locked();
-            break;
+bool LogReader::DoSocketDelete(SocketClient* cli) {
+    auto cli_name = SocketClientToName(cli);
+    auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
+    for (const auto& reader : reader_list_->reader_threads()) {
+        if (reader->name() == cli_name) {
+            reader->release_Locked();
+            return true;
         }
-        it++;
     }
-    LogReaderThread::unlock();
+    return false;
 }
 
 int LogReader::getLogSocket() {
diff --git a/logd/LogReader.h b/logd/LogReader.h
index f00cc21..b85a584 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -18,28 +18,24 @@
 
 #include <sysutils/SocketListener.h>
 
+#include "LogBuffer.h"
+#include "LogReaderList.h"
 #include "LogReaderThread.h"
 
 #define LOGD_SNDTIMEO 32
 
-class LogBuffer;
-
 class LogReader : public SocketListener {
-    LogBuffer& mLogbuf;
+  public:
+    explicit LogReader(LogBuffer* logbuf, LogReaderList* reader_list);
 
-   public:
-    explicit LogReader(LogBuffer* logbuf);
-    void notifyNewLog(unsigned int logMask);
-
-    LogBuffer& logbuf(void) const {
-        return mLogbuf;
-    }
-
-   protected:
+  protected:
     virtual bool onDataAvailable(SocketClient* cli);
 
-   private:
+  private:
     static int getLogSocket();
 
-    void doSocketDelete(SocketClient* cli);
+    bool DoSocketDelete(SocketClient* cli);
+
+    LogBuffer* log_buffer_;
+    LogReaderList* reader_list_;
 };
diff --git a/logd/LogReaderList.cpp b/logd/LogReaderList.cpp
new file mode 100644
index 0000000..220027b
--- /dev/null
+++ b/logd/LogReaderList.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 "LogReaderList.h"
+
+// When we are notified a new log entry is available, inform
+// listening sockets who are watching this entry's log id.
+void LogReaderList::NotifyNewLog(unsigned int log_mask) const {
+    auto lock = std::lock_guard{reader_threads_lock_};
+
+    for (const auto& entry : reader_threads_) {
+        if (!entry->IsWatchingMultiple(log_mask)) {
+            continue;
+        }
+        if (entry->deadline().time_since_epoch().count() != 0) {
+            continue;
+        }
+        entry->triggerReader_Locked();
+    }
+}
diff --git a/logd/LogReaderList.h b/logd/LogReaderList.h
new file mode 100644
index 0000000..0d84aba
--- /dev/null
+++ b/logd/LogReaderList.h
@@ -0,0 +1,35 @@
+/*
+ * 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 <list>
+#include <memory>
+#include <mutex>
+
+#include "LogReaderThread.h"
+
+class LogReaderList {
+  public:
+    void NotifyNewLog(unsigned int log_mask) const;
+
+    std::list<std::unique_ptr<LogReaderThread>>& reader_threads() { return reader_threads_; }
+    std::mutex& reader_threads_lock() { return reader_threads_lock_; }
+
+  private:
+    std::list<std::unique_ptr<LogReaderThread>> reader_threads_;
+    mutable std::mutex reader_threads_lock_;
+};
\ No newline at end of file
diff --git a/logd/LogReaderThread.cpp b/logd/LogReaderThread.cpp
index 5413c4d..b2001b5 100644
--- a/logd/LogReaderThread.cpp
+++ b/logd/LogReaderThread.cpp
@@ -23,78 +23,65 @@
 #include <thread>
 
 #include "LogBuffer.h"
-#include "LogReader.h"
+#include "LogReaderList.h"
 
 using namespace std::placeholders;
 
-pthread_mutex_t LogReaderThread::timesLock = PTHREAD_MUTEX_INITIALIZER;
-
-LogReaderThread::LogReaderThread(LogReader& reader, SocketClient* client, bool non_block,
+LogReaderThread::LogReaderThread(LogBuffer* log_buffer, LogReaderList* reader_list,
+                                 std::unique_ptr<LogWriter> writer, bool non_block,
                                  unsigned long tail, unsigned int log_mask, pid_t pid,
-                                 log_time start_time, uint64_t start, uint64_t timeout,
-                                 bool privileged, bool can_read_security_logs)
-    : leading_dropped_(false),
-      reader_(reader),
+                                 log_time start_time, uint64_t start,
+                                 std::chrono::steady_clock::time_point deadline)
+    : log_buffer_(log_buffer),
+      reader_list_(reader_list),
+      writer_(std::move(writer)),
+      leading_dropped_(false),
       log_mask_(log_mask),
       pid_(pid),
       tail_(tail),
       count_(0),
       index_(0),
-      client_(client),
       start_time_(start_time),
       start_(start),
-      non_block_(non_block),
-      privileged_(privileged),
-      can_read_security_logs_(can_read_security_logs) {
-    timeout_.tv_sec = timeout / NS_PER_SEC;
-    timeout_.tv_nsec = timeout % NS_PER_SEC;
+      deadline_(deadline),
+      non_block_(non_block) {
     memset(last_tid_, 0, sizeof(last_tid_));
-    pthread_cond_init(&thread_triggered_condition_, nullptr);
     cleanSkip_Locked();
-}
-
-bool LogReaderThread::startReader_Locked() {
     auto thread = std::thread{&LogReaderThread::ThreadFunction, this};
     thread.detach();
-    return true;
 }
 
 void LogReaderThread::ThreadFunction() {
     prctl(PR_SET_NAME, "logd.reader.per");
 
-    SocketClient* client = client_;
-
-    LogBuffer& logbuf = reader_.logbuf();
-
     leading_dropped_ = true;
 
-    wrlock();
+    auto lock = std::unique_lock{reader_list_->reader_threads_lock()};
 
     uint64_t start = start_;
 
     while (!release_) {
-        if (timeout_.tv_sec || timeout_.tv_nsec) {
-            if (pthread_cond_clockwait(&thread_triggered_condition_, &timesLock, CLOCK_MONOTONIC,
-                                       &timeout_) == ETIMEDOUT) {
-                timeout_.tv_sec = 0;
-                timeout_.tv_nsec = 0;
+        if (deadline_.time_since_epoch().count() != 0) {
+            if (thread_triggered_condition_.wait_until(lock, deadline_) ==
+                std::cv_status::timeout) {
+                deadline_ = {};
             }
             if (release_) {
                 break;
             }
         }
 
-        unlock();
+        lock.unlock();
 
         if (tail_) {
-            logbuf.flushTo(client, start, nullptr, privileged_, can_read_security_logs_,
-                           std::bind(&LogReaderThread::FilterFirstPass, this, _1));
+            log_buffer_->FlushTo(writer_.get(), start, nullptr,
+                                 std::bind(&LogReaderThread::FilterFirstPass, this, _1));
             leading_dropped_ =
                     true;  // TODO: Likely a bug, if leading_dropped_ was not true before calling
                            // flushTo(), then it should not be reset to true after.
         }
-        start = logbuf.flushTo(client, start, last_tid_, privileged_, can_read_security_logs_,
-                               std::bind(&LogReaderThread::FilterSecondPass, this, _1));
+        start = log_buffer_->FlushTo(writer_.get(), start, last_tid_,
+                                     std::bind(&LogReaderThread::FilterSecondPass, this, _1));
 
         // We only ignore entries before the original start time for the first flushTo(), if we
         // get entries after this first flush before the original start time, then the client
@@ -105,9 +92,9 @@
         start_time_.tv_sec = 0;
         start_time_.tv_nsec = 0;
 
-        wrlock();
+        lock.lock();
 
-        if (start == LogBufferElement::FLUSH_ERROR) {
+        if (start == LogBuffer::FLUSH_ERROR) {
             break;
         }
 
@@ -119,35 +106,29 @@
 
         cleanSkip_Locked();
 
-        if (!timeout_.tv_sec && !timeout_.tv_nsec) {
-            pthread_cond_wait(&thread_triggered_condition_, &timesLock);
+        if (deadline_.time_since_epoch().count() == 0) {
+            thread_triggered_condition_.wait(lock);
         }
     }
 
-    LogReader& reader = reader_;
-    reader.release(client);
+    writer_->Release();
 
-    client->decRef();
-
-    LastLogTimes& times = reader.logbuf().mTimes;
-    auto it = std::find_if(times.begin(), times.end(),
+    auto& log_reader_threads = reader_list_->reader_threads();
+    auto it = std::find_if(log_reader_threads.begin(), log_reader_threads.end(),
                            [this](const auto& other) { return other.get() == this; });
 
-    if (it != times.end()) {
-        times.erase(it);
+    if (it != log_reader_threads.end()) {
+        log_reader_threads.erase(it);
     }
-
-    unlock();
 }
 
 // A first pass to count the number of elements
-int LogReaderThread::FilterFirstPass(const LogBufferElement* element) {
-    LogReaderThread::wrlock();
+FlushToResult LogReaderThread::FilterFirstPass(const LogBufferElement* element) {
+    auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
 
     if (leading_dropped_) {
         if (element->getDropped()) {
-            LogReaderThread::unlock();
-            return false;
+            return FlushToResult::kSkip;
         }
         leading_dropped_ = false;
     }
@@ -161,48 +142,46 @@
         ++count_;
     }
 
-    LogReaderThread::unlock();
-
-    return false;
+    return FlushToResult::kSkip;
 }
 
 // A second pass to send the selected elements
-int LogReaderThread::FilterSecondPass(const LogBufferElement* element) {
-    LogReaderThread::wrlock();
+FlushToResult LogReaderThread::FilterSecondPass(const LogBufferElement* element) {
+    auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
 
     start_ = element->getSequence();
 
     if (skip_ahead_[element->getLogId()]) {
         skip_ahead_[element->getLogId()]--;
-        goto skip;
+        return FlushToResult::kSkip;
     }
 
     if (leading_dropped_) {
         if (element->getDropped()) {
-            goto skip;
+            return FlushToResult::kSkip;
         }
         leading_dropped_ = false;
     }
 
     // Truncate to close race between first and second pass
     if (non_block_ && tail_ && index_ >= count_) {
-        goto stop;
+        return FlushToResult::kStop;
     }
 
     if (!IsWatching(element->getLogId())) {
-        goto skip;
+        return FlushToResult::kSkip;
     }
 
     if (pid_ && pid_ != element->getPid()) {
-        goto skip;
+        return FlushToResult::kSkip;
     }
 
     if (start_time_ != log_time::EPOCH && element->getRealTime() <= start_time_) {
-        goto skip;
+        return FlushToResult::kSkip;
     }
 
     if (release_) {
-        goto stop;
+        return FlushToResult::kStop;
     }
 
     if (!tail_) {
@@ -212,7 +191,7 @@
     ++index_;
 
     if (count_ > tail_ && index_ <= (count_ - tail_)) {
-        goto skip;
+        return FlushToResult::kSkip;
     }
 
     if (!non_block_) {
@@ -221,18 +200,9 @@
 
 ok:
     if (!skip_ahead_[element->getLogId()]) {
-        LogReaderThread::unlock();
-        return true;
+        return FlushToResult::kWrite;
     }
-    // FALLTHRU
-
-skip:
-    LogReaderThread::unlock();
-    return false;
-
-stop:
-    LogReaderThread::unlock();
-    return -1;
+    return FlushToResult::kSkip;
 }
 
 void LogReaderThread::cleanSkip_Locked(void) {
diff --git a/logd/LogReaderThread.h b/logd/LogReaderThread.h
index 39a8b63..e48a3ca 100644
--- a/logd/LogReaderThread.h
+++ b/logd/LogReaderThread.h
@@ -21,54 +21,55 @@
 #include <sys/types.h>
 #include <time.h>
 
+#include <chrono>
+#include <condition_variable>
 #include <list>
 #include <memory>
 
 #include <log/log.h>
 #include <sysutils/SocketClient.h>
 
-class LogReader;
-class LogBufferElement;
+#include "LogBuffer.h"
+#include "LogBufferElement.h"
+#include "LogWriter.h"
+
+class LogReaderList;
 
 class LogReaderThread {
-    static pthread_mutex_t timesLock;
-
   public:
-    LogReaderThread(LogReader& reader, SocketClient* client, bool non_block, unsigned long tail,
+    LogReaderThread(LogBuffer* log_buffer, LogReaderList* reader_list,
+                    std::unique_ptr<LogWriter> writer, bool non_block, unsigned long tail,
                     unsigned int log_mask, pid_t pid, log_time start_time, uint64_t sequence,
-                    uint64_t timeout, bool privileged, bool can_read_security_logs);
-
-    // Protect List manipulations
-    static void wrlock() { pthread_mutex_lock(&timesLock); }
-    static void rdlock() { pthread_mutex_lock(&timesLock); }
-    static void unlock() { pthread_mutex_unlock(&timesLock); }
-
-    bool startReader_Locked();
-
-    void triggerReader_Locked() { pthread_cond_signal(&thread_triggered_condition_); }
+                    std::chrono::steady_clock::time_point deadline);
+    void triggerReader_Locked() { thread_triggered_condition_.notify_all(); }
 
     void triggerSkip_Locked(log_id_t id, unsigned int skip) { skip_ahead_[id] = skip; }
     void cleanSkip_Locked();
 
     void release_Locked() {
         // gracefully shut down the socket.
-        shutdown(client_->getSocket(), SHUT_RDWR);
+        writer_->Shutdown();
         release_ = true;
-        pthread_cond_signal(&thread_triggered_condition_);
+        thread_triggered_condition_.notify_all();
     }
 
     bool IsWatching(log_id_t id) const { return log_mask_ & (1 << id); }
     bool IsWatchingMultiple(unsigned int log_mask) const { return log_mask_ & log_mask; }
 
-    const SocketClient* client() const { return client_; }
+    std::string name() const { return writer_->name(); }
     uint64_t start() const { return start_; }
-    const timespec& timeout() const { return timeout_; }
+    std::chrono::steady_clock::time_point deadline() const { return deadline_; }
 
   private:
     void ThreadFunction();
     // flushTo filter callbacks
-    int FilterFirstPass(const LogBufferElement* element);
-    int FilterSecondPass(const LogBufferElement* element);
+    FlushToResult FilterFirstPass(const LogBufferElement* element);
+    FlushToResult FilterSecondPass(const LogBufferElement* element);
+
+    std::condition_variable thread_triggered_condition_;
+    LogBuffer* log_buffer_;
+    LogReaderList* reader_list_;
+    std::unique_ptr<LogWriter> writer_;
 
     // Set to true to cause the thread to end and the LogReaderThread to delete itself.
     bool release_ = false;
@@ -76,11 +77,6 @@
     // messages should be ignored.
     bool leading_dropped_;
 
-    // Condition variable for waking the reader thread if there are messages pending for its client.
-    pthread_cond_t thread_triggered_condition_;
-
-    // Reference to the parent thread that manages log reader sockets.
-    LogReader& reader_;
     // A mask of the logs buffers that are read by this reader.
     const unsigned int log_mask_;
     // If set to non-zero, only pids equal to this are read by the reader.
@@ -103,24 +99,14 @@
     // and to disconnect the reader (if it is dumpAndClose, `adb logcat -t`), when index_ >= count_.
     unsigned long index_;
 
-    // A pointer to the socket for this reader.
-    SocketClient* client_;
     // When a reader requests logs starting from a given timestamp, its stored here for the first
     // pass, such that logs before this time stamp that are accumulated in the buffer are ignored.
     log_time start_time_;
     // The point from which the reader will read logs once awoken.
     uint64_t start_;
-    // CLOCK_MONOTONIC based timeout used for log wrapping.  If this timeout expires before logs
+    // CLOCK_MONOTONIC based deadline used for log wrapping.  If this deadline expires before logs
     // wrap, then wake up and send the logs to the reader anyway.
-    timespec timeout_;
+    std::chrono::steady_clock::time_point deadline_;
     // If this reader is 'dumpAndClose' and will disconnect once it has read its intended logs.
     const bool non_block_;
-
-    // Whether or not this reader can read logs from all UIDs or only its own UID.  See
-    // clientHasLogCredentials().
-    bool privileged_;
-    // Whether or not this reader can read security logs.  See CanReadSecurityLogs().
-    bool can_read_security_logs_;
 };
-
-typedef std::list<std::unique_ptr<LogReaderThread>> LastLogTimes;
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index 3fe1bbe..ce82b41 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -60,10 +60,6 @@
 }
 }
 
-// Furnished in LogCommand.cpp
-bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid);
-bool clientHasLogCredentials(SocketClient* cli);
-
 static inline bool worstUidEnabledForLogid(log_id_t id) {
     return (id == LOG_ID_MAIN) || (id == LOG_ID_SYSTEM) ||
            (id == LOG_ID_RADIO) || (id == LOG_ID_EVENTS);
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index 9d762dc..88a3bdc 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -16,13 +16,11 @@
 
 #include <ctype.h>
 
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
-#include <cutils/properties.h>
 
 #include "LogWhiteBlackList.h"
 
-// White and Black list
-
 Prune::Prune(uid_t uid, pid_t pid) : mUid(uid), mPid(pid) {
 }
 
@@ -75,14 +73,12 @@
         it = mNaughty.erase(it);
     }
 
-    static const char _default[] = "default";
     // default here means take ro.logd.filter, persist.logd.filter then
     // internal default in that order.
-    if (str && !strcmp(str, _default)) {
+    if (str && !strcmp(str, "default")) {
         str = nullptr;
     }
-    static const char _disable[] = "disable";
-    if (str && !strcmp(str, _disable)) {
+    if (str && !strcmp(str, "disable")) {
         str = "";
     }
 
@@ -91,22 +87,20 @@
     if (str) {
         filter = str;
     } else {
-        char property[PROPERTY_VALUE_MAX];
-        property_get("ro.logd.filter", property, _default);
-        filter = property;
-        property_get("persist.logd.filter", property, filter.c_str());
+        filter = android::base::GetProperty("ro.logd.filter", "default");
+        auto persist_filter = android::base::GetProperty("persist.logd.filter", "default");
         // default here means take ro.logd.filter
-        if (strcmp(property, _default)) {
-            filter = property;
+        if (persist_filter != "default") {
+            filter = persist_filter;
         }
     }
 
     // default here means take internal default.
-    if (filter == _default) {
+    if (filter == "default") {
         // See README.property for description of filter format
         filter = "~! ~1000/!";
     }
-    if (filter == _disable) {
+    if (filter == "disable") {
         filter = "";
     }
 
diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h
index 6e9893b..0e4e837 100644
--- a/logd/LogWhiteBlackList.h
+++ b/logd/LogWhiteBlackList.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LOGD_LOG_WHITE_BLACK_LIST_H__
-#define _LOGD_LOG_WHITE_BLACK_LIST_H__
+#pragma once
 
 #include <sys/types.h>
 
@@ -24,8 +23,6 @@
 
 #include "LogBufferElement.h"
 
-// White and Blacklist
-
 class Prune {
     friend class PruneList;
 
@@ -84,5 +81,3 @@
 
     std::string format();
 };
-
-#endif  // _LOGD_LOG_WHITE_BLACK_LIST_H__
diff --git a/logd/LogWriter.h b/logd/LogWriter.h
new file mode 100644
index 0000000..b6c5b67
--- /dev/null
+++ b/logd/LogWriter.h
@@ -0,0 +1,46 @@
+/*
+ * 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 <string>
+
+#include <log/log_read.h>
+
+// An interface for writing logs to a reader.
+class LogWriter {
+  public:
+    LogWriter(uid_t uid, bool privileged, bool can_read_security_logs)
+        : uid_(uid), privileged_(privileged), can_read_security_logs_(can_read_security_logs) {}
+    virtual ~LogWriter() {}
+
+    virtual bool Write(const logger_entry& entry, const char* msg) = 0;
+    virtual void Shutdown() {}
+    virtual void Release() {}
+
+    virtual std::string name() const = 0;
+    uid_t uid() const { return uid_; }
+
+    bool privileged() const { return privileged_; }
+    bool can_read_security_logs() const { return can_read_security_logs_; }
+
+  private:
+    uid_t uid_;
+
+    // If this writer sees logs from all UIDs or only its own UID.  See clientHasLogCredentials().
+    bool privileged_;
+    bool can_read_security_logs_;  // If this writer sees security logs.  See CanReadSecurityLogs().
+};
\ No newline at end of file
diff --git a/logd/fuzz/Android.bp b/logd/fuzz/Android.bp
index 299242d..f65fbdf 100644
--- a/logd/fuzz/Android.bp
+++ b/logd/fuzz/Android.bp
@@ -25,6 +25,7 @@
         "liblog",
         "liblogd",
         "libcutils",
+        "libsysutils",
     ],
     cflags: ["-Werror"],
 }
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index 58610c4..8f90f50 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -15,7 +15,8 @@
  */
 #include <string>
 
-#include "../LogBuffer.h"
+#include "../ChattyLogBuffer.h"
+#include "../LogReaderList.h"
 #include "../LogReaderThread.h"
 #include "../LogStatistics.h"
 
@@ -71,7 +72,7 @@
 
     // Other elements not in enum.
     log_id_t log_id = static_cast<log_id_t>(unsigned(logInput->log_id) % (LOG_ID_MAX + 1));
-    log_buffer->log(log_id, logInput->realtime, logInput->uid, logInput->pid, logInput->tid, msg,
+    log_buffer->Log(log_id, logInput->realtime, logInput->uid, logInput->pid, logInput->tid, msg,
                     sizeof(uint32_t) + msg_length + 1);
     stats->Format(logInput->uid, logInput->pid, logInput->log_mask);
     *pdata = data;
@@ -95,25 +96,25 @@
         return 0;
     }
 
-    LastLogTimes times;
+    LogReaderList reader_list;
     LogTags tags;
     PruneList prune_list;
     LogStatistics stats(true);
-    LogBuffer log_buffer(&times, &tags, &prune_list, &stats);
+    LogBuffer* log_buffer = new ChattyLogBuffer(&reader_list, &tags, &prune_list, &stats);
     size_t data_left = size;
     const uint8_t** pdata = &data;
 
     prune_list.init(nullptr);
     // We want to get pruning code to get called.
-    log_id_for_each(i) { log_buffer.setSize(i, 10000); }
+    log_id_for_each(i) { log_buffer->SetSize(i, 10000); }
 
     while (data_left >= sizeof(LogInput) + 2 * sizeof(uint8_t)) {
-        if (!write_log_messages(pdata, &data_left, &log_buffer, &stats)) {
+        if (!write_log_messages(pdata, &data_left, log_buffer, &stats)) {
             return 0;
         }
     }
 
-    log_id_for_each(i) { log_buffer.clear(i); }
+    log_id_for_each(i) { log_buffer->Clear(i, 0); }
     return 0;
 }
 }  // namespace android
diff --git a/logd/libaudit.c b/logd/libaudit.cpp
similarity index 67%
rename from logd/libaudit.c
rename to logd/libaudit.cpp
index f452c71..ccea0a2 100644
--- a/logd/libaudit.c
+++ b/logd/libaudit.cpp
@@ -18,11 +18,13 @@
  *
  */
 
+#include "libaudit.h"
+
 #include <errno.h>
 #include <string.h>
 #include <unistd.h>
 
-#include "libaudit.h"
+#include <limits>
 
 /**
  * Waits for an ack from the kernel
@@ -32,22 +34,15 @@
  *  This function returns 0 on success, else -errno.
  */
 static int get_ack(int fd) {
-    int rc;
-    struct audit_message rep;
-
-    /* Sanity check, this is an internal interface this shouldn't happen */
-    if (fd < 0) {
-        return -EINVAL;
-    }
-
-    rc = audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, MSG_PEEK);
+    struct audit_message rep = {};
+    int rc = audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, MSG_PEEK);
     if (rc < 0) {
         return rc;
     }
 
     if (rep.nlh.nlmsg_type == NLMSG_ERROR) {
         audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, 0);
-        rc = ((struct nlmsgerr*)rep.data)->error;
+        rc = reinterpret_cast<struct nlmsgerr*>(rep.data)->error;
         if (rc) {
             return -rc;
         }
@@ -70,19 +65,11 @@
  *  This function returns a positive sequence number on success, else -errno.
  */
 static int audit_send(int fd, int type, const void* data, size_t size) {
-    int rc;
-    static int16_t sequence = 0;
-    struct audit_message req;
-    struct sockaddr_nl addr;
-
-    memset(&req, 0, sizeof(req));
-    memset(&addr, 0, sizeof(addr));
-
-    /* We always send netlink messaged */
-    addr.nl_family = AF_NETLINK;
+    struct sockaddr_nl addr = {.nl_family = AF_NETLINK};
 
     /* Set up the netlink headers */
-    req.nlh.nlmsg_type = type;
+    struct audit_message req = {};
+    req.nlh.nlmsg_type = static_cast<uint16_t>(type);
     req.nlh.nlmsg_len = NLMSG_SPACE(size);
     req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
 
@@ -107,29 +94,23 @@
     /*
      * Only increment the sequence number on a guarantee
      * you will send it to the kernel.
-     *
-     * Also, the sequence is defined as a u32 in the kernel
-     * struct. Using an int here might not work on 32/64 bit splits. A
-     * signed 64 bit value can overflow a u32..but a u32
-     * might not fit in the response, so we need to use s32.
-     * Which is still kind of hackish since int could be 16 bits
-     * in size. The only safe type to use here is a signed 16
-     * bit value.
      */
-    req.nlh.nlmsg_seq = ++sequence;
+    static uint32_t sequence = 0;
+    if (sequence == std::numeric_limits<uint32_t>::max()) {
+        sequence = 1;
+    } else {
+        sequence++;
+    }
+    req.nlh.nlmsg_seq = sequence;
 
-    /* While failing and its due to interrupts */
-
-    rc = TEMP_FAILURE_RETRY(sendto(fd, &req, req.nlh.nlmsg_len, 0,
-                                   (struct sockaddr*)&addr, sizeof(addr)));
+    ssize_t rc = TEMP_FAILURE_RETRY(
+            sendto(fd, &req, req.nlh.nlmsg_len, 0, (struct sockaddr*)&addr, sizeof(addr)));
 
     /* Not all the bytes were sent */
     if (rc < 0) {
-        rc = -errno;
-        goto out;
+        return -errno;
     } else if ((uint32_t)rc != req.nlh.nlmsg_len) {
-        rc = -EPROTO;
-        goto out;
+        return -EPROTO;
     }
 
     /* We sent all the bytes, get the ack */
@@ -138,32 +119,22 @@
     /* If the ack failed, return the error, else return the sequence number */
     rc = (rc == 0) ? (int)sequence : rc;
 
-out:
-    /* Don't let sequence roll to negative */
-    if (sequence < 0) {
-        sequence = 0;
-    }
-
     return rc;
 }
 
 int audit_setup(int fd, pid_t pid) {
-    int rc;
-    struct audit_message rep;
-    struct audit_status status;
-
-    memset(&status, 0, sizeof(status));
-
     /*
      * In order to set the auditd PID we send an audit message over the netlink
      * socket with the pid field of the status struct set to our current pid,
      * and the the mask set to AUDIT_STATUS_PID
      */
-    status.pid = pid;
-    status.mask = AUDIT_STATUS_PID;
+    struct audit_status status = {
+            .mask = AUDIT_STATUS_PID,
+            .pid = static_cast<uint32_t>(pid),
+    };
 
     /* Let the kernel know this pid will be registering for audit events */
-    rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
+    int rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
     if (rc < 0) {
         return rc;
     }
@@ -178,6 +149,7 @@
      * so I went to non-blocking and it seemed to fix the bug.
      * Need to investigate further.
      */
+    struct audit_message rep = {};
     audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);
 
     return 0;
@@ -188,27 +160,18 @@
 }
 
 int audit_rate_limit(int fd, uint32_t limit) {
-    struct audit_status status;
-    memset(&status, 0, sizeof(status));
-    status.mask = AUDIT_STATUS_RATE_LIMIT;
-    status.rate_limit = limit; /* audit entries per second */
+    struct audit_status status = {
+            .mask = AUDIT_STATUS_RATE_LIMIT, .rate_limit = limit, /* audit entries per second */
+    };
     return audit_send(fd, AUDIT_SET, &status, sizeof(status));
 }
 
 int audit_get_reply(int fd, struct audit_message* rep, reply_t block, int peek) {
-    ssize_t len;
-    int flags;
-    int rc = 0;
-
-    struct sockaddr_nl nladdr;
-    socklen_t nladdrlen = sizeof(nladdr);
-
     if (fd < 0) {
         return -EBADF;
     }
 
-    /* Set up the flags for recv from */
-    flags = (block == GET_REPLY_NONBLOCKING) ? MSG_DONTWAIT : 0;
+    int flags = (block == GET_REPLY_NONBLOCKING) ? MSG_DONTWAIT : 0;
     flags |= peek;
 
     /*
@@ -216,19 +179,20 @@
      * the interface shows that EINTR can never be returned, other errors,
      * however, can be returned.
      */
-    len = TEMP_FAILURE_RETRY(recvfrom(fd, rep, sizeof(*rep), flags,
-                                      (struct sockaddr*)&nladdr, &nladdrlen));
+    struct sockaddr_nl nladdr;
+    socklen_t nladdrlen = sizeof(nladdr);
+    ssize_t len = TEMP_FAILURE_RETRY(
+            recvfrom(fd, rep, sizeof(*rep), flags, (struct sockaddr*)&nladdr, &nladdrlen));
 
     /*
      * EAGAIN should be re-tried until success or another error manifests.
      */
     if (len < 0) {
-        rc = -errno;
-        if (block == GET_REPLY_NONBLOCKING && rc == -EAGAIN) {
+        if (block == GET_REPLY_NONBLOCKING && errno == EAGAIN) {
             /* If request is non blocking and errno is EAGAIN, just return 0 */
             return 0;
         }
-        return rc;
+        return -errno;
     }
 
     if (nladdrlen != sizeof(nladdr)) {
@@ -242,10 +206,10 @@
 
     /* Check if the reply from the kernel was ok */
     if (!NLMSG_OK(&rep->nlh, (size_t)len)) {
-        rc = (len == sizeof(*rep)) ? -EFBIG : -EBADE;
+        return len == sizeof(*rep) ? -EFBIG : -EBADE;
     }
 
-    return rc;
+    return 0;
 }
 
 void audit_close(int fd) {
diff --git a/logd/libaudit.h b/logd/libaudit.h
index b4a92a8..27b0866 100644
--- a/logd/libaudit.h
+++ b/logd/libaudit.h
@@ -17,8 +17,7 @@
  * Written by William Roberts <w.roberts@sta.samsung.com>
  */
 
-#ifndef _LIBAUDIT_H_
-#define _LIBAUDIT_H_
+#pragma once
 
 #include <stdint.h>
 #include <sys/cdefs.h>
@@ -102,5 +101,3 @@
 extern int audit_rate_limit(int fd, uint32_t limit);
 
 __END_DECLS
-
-#endif
diff --git a/logd/main.cpp b/logd/main.cpp
index cd8b195..1deca72 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -38,7 +38,6 @@
 
 #include <android-base/macros.h>
 #include <cutils/android_get_control_file.h>
-#include <cutils/properties.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
 #include <packagelistparser/packagelistparser.h>
@@ -47,11 +46,13 @@
 #include <processgroup/sched_policy.h>
 #include <utils/threads.h>
 
+#include "ChattyLogBuffer.h"
 #include "CommandListener.h"
 #include "LogAudit.h"
 #include "LogBuffer.h"
 #include "LogKlog.h"
 #include "LogListener.h"
+#include "LogReader.h"
 #include "LogStatistics.h"
 #include "LogTags.h"
 #include "LogUtils.h"
@@ -271,8 +272,10 @@
 
     // A cache of event log tags
     LogTags log_tags;
+
     // Pruning configuration.
     PruneList prune_list;
+
     // Partial (required for chatty) or full logging statistics.
     bool enable_full_log_statistics = __android_logger_property_get_bool(
             "logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
@@ -282,18 +285,15 @@
     // Serves the purpose of managing the last logs times read on a
     // socket connection, and as a reader lock on a range of log
     // entries.
-
-    LastLogTimes* times = new LastLogTimes();
+    LogReaderList reader_list;
 
     // LogBuffer is the object which is responsible for holding all
     // log entries.
-
-    LogBuffer* logBuf = new LogBuffer(times, &log_tags, &prune_list, &log_statistics);
+    LogBuffer* logBuf = new ChattyLogBuffer(&reader_list, &log_tags, &prune_list, &log_statistics);
 
     // LogReader listens on /dev/socket/logdr. When a client
     // connects, log entries in the LogBuffer are written to the client.
-
-    LogReader* reader = new LogReader(logBuf);
+    LogReader* reader = new LogReader(logBuf, &reader_list);
     if (reader->startListener()) {
         return EXIT_FAILURE;
     }
@@ -301,15 +301,13 @@
     // LogListener listens on /dev/socket/logdw for client
     // initiated log messages. New log entries are added to LogBuffer
     // and LogReader is notified to send updates to connected clients.
-
-    LogListener* swl = new LogListener(logBuf, reader);
+    LogListener* swl = new LogListener(logBuf);
     if (!swl->StartListener()) {
         return EXIT_FAILURE;
     }
 
     // Command listener listens on /dev/socket/logd for incoming logd
     // administrative commands.
-
     CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list, &log_statistics);
     if (cl->startListener()) {
         return EXIT_FAILURE;
@@ -318,26 +316,22 @@
     // LogAudit listens on NETLINK_AUDIT socket for selinux
     // initiated log messages. New log entries are added to LogBuffer
     // and LogReader is notified to send updates to connected clients.
-
     LogAudit* al = nullptr;
     if (auditd) {
-        al = new LogAudit(
-                logBuf, reader,
-                __android_logger_property_get_bool("ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
-                        ? fdDmesg
-                        : -1,
-                &log_statistics);
+        int dmesg_fd = __android_logger_property_get_bool("ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
+                               ? fdDmesg
+                               : -1;
+        al = new LogAudit(logBuf, dmesg_fd, &log_statistics);
     }
 
     LogKlog* kl = nullptr;
     if (klogd) {
-        kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != nullptr, &log_statistics);
+        kl = new LogKlog(logBuf, fdDmesg, fdPmesg, al != nullptr, &log_statistics);
     }
 
     readDmesg(al, kl);
 
     // failure is an option ... messages are in dmesg (required by standard)
-
     if (kl && kl->startListener()) {
         delete kl;
     }
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a380ebb..00a58bf 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -547,8 +547,8 @@
     enter_default_mount_ns
 
     # /data/apex is now available. Start apexd to scan and activate APEXes.
-    mkdir /data/apex 0750 root system encryption=None
-    mkdir /data/apex/active 0750 root system
+    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