Merge "Add fuzzers for libutils classes"
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_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/fastboot/Android.bp b/fastboot/Android.bp
index 6b49fc7..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,6 +145,7 @@
     ],
 
     static_libs: [
+        "libgtest_prod",
         "libhealthhalutils",
         "libsnapshot_nobinder",
         "update_metadata-protos",
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/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 6cd0430..676f446 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -14,9 +14,6 @@
     },
     {
       "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 2783e4d..c191102 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -246,8 +246,8 @@
     gtest: false,
 }
 
-cc_defaults {
-    name: "libsnapshot_fuzzer_defaults",
+cc_fuzz {
+    name: "libsnapshot_fuzzer",
 
     // TODO(b/154633114): make host supported.
     // host_supported: true,
@@ -289,11 +289,6 @@
         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"],
@@ -303,14 +298,3 @@
         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/fuzz_utils.h b/fs_mgr/libsnapshot/fuzz_utils.h
index 20b13b2..4dc6cdc 100644
--- a/fs_mgr/libsnapshot/fuzz_utils.h
+++ b/fs_mgr/libsnapshot/fuzz_utils.h
@@ -68,25 +68,17 @@
     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>
-const google::protobuf::FieldDescriptor* GetValueFieldDescriptor(
-        const typename Action::Proto& action_proto) {
+void ExecuteActionProto(typename Action::Class* module,
+                        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 nullptr;
+        return;
     }
-    return 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;
+    const auto* field_desc = action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc);
     auto number = field_desc->number();
     const auto& map = *Action::GetFunctionMap();
     auto it = map.find(number);
@@ -97,7 +89,7 @@
 
 template <typename Action>
 void ExecuteAllActionProtos(
-        typename Action::ClassType* module,
+        typename Action::Class* module,
         const google::protobuf::RepeatedPtrField<typename Action::Proto>& action_protos) {
     for (const auto& proto : action_protos) {
         ExecuteActionProto<Action>(module, proto);
@@ -142,57 +134,53 @@
 // ActionPerformer extracts arguments from the protobuf message, and then call FuzzFunction
 // with these arguments.
 template <typename FuzzFunction, typename Signature, typename Enabled = void>
-struct ActionPerformerImpl;  // undefined
+struct ActionPerfomer;  // undefined
 
 template <typename FuzzFunction, typename MessageProto>
-struct ActionPerformerImpl<
+struct ActionPerfomer<
         FuzzFunction, void(const MessageProto&),
         typename std::enable_if_t<std::is_base_of_v<google::protobuf::Message, MessageProto>>> {
-    static typename FuzzFunction::ReturnType Invoke(
-            typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
-            const google::protobuf::FieldDescriptor* field_desc) {
+    static void Invoke(typename FuzzFunction::Class* 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));
-        return FuzzFunction::ImplBody(module, arg);
+        FuzzFunction::ImplBody(module, arg);
     }
 };
 
 template <typename FuzzFunction, typename Primitive>
-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) {
+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) {
         Primitive arg = std::invoke(PrimitiveGetter<Primitive>::fp, action_proto.GetReflection(),
                                     action_proto, field_desc);
-        return FuzzFunction::ImplBody(module, arg);
+        FuzzFunction::ImplBody(module, arg);
     }
 };
 
 template <typename FuzzFunction>
-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);
+struct ActionPerfomer<FuzzFunction, void()> {
+    static void Invoke(typename FuzzFunction::Class* module, const google::protobuf::Message&,
+                       const google::protobuf::FieldDescriptor*) {
+        FuzzFunction::ImplBody(module);
     }
 };
 
 template <typename FuzzFunction>
-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) {
+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) {
         std::string scratch;
         const std::string& arg = action_proto.GetReflection()->GetStringReference(
                 action_proto, field_desc, &scratch);
-        return FuzzFunction::ImplBody(module, arg);
+        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.
@@ -209,11 +197,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(Class, Action)                                                                \
+#define FUZZ_CLASS(ClassType, Action)                                                            \
     class Action {                                                                               \
       public:                                                                                    \
         using Proto = Action##Proto;                                                             \
-        using ClassType = Class;                                                                 \
+        using Class = ClassType;                                                                 \
         using FunctionMap = android::fuzz::FunctionMap<Class>;                                   \
         static FunctionMap* GetFunctionMap() {                                                   \
             static Action::FunctionMap map;                                                      \
@@ -237,33 +225,29 @@
 // }
 // class Foo { public: void DoAwesomeFoo(bool arg); };
 // FUZZ_OBJECT(FooAction, Foo);
-// FUZZ_FUNCTION(FooAction, DoFoo, void, IFoo* module, bool arg) {
+// FUZZ_FUNCTION(FooAction, DoFoo, 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, 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__)
+#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__)
 
 // Implement a simple action by linking it to the function with the same name. Example:
 // message FooActionProto {
@@ -277,9 +261,5 @@
 // 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,                                       \
-                  decltype(std::declval<Action::ClassType>().FunctionName()), \
-                  Action::ClassType* module) {                                \
-        return module->FunctionName();                                        \
-    }
+#define FUZZ_SIMPLE_FUNCTION(Action, FunctionName) \
+    FUZZ_FUNCTION(Action, FunctionName, module) { (void)module->FunctionName(); }
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
index 5b145c3..1e90ace 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
@@ -21,21 +21,14 @@
 #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;
@@ -44,8 +37,6 @@
 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.
@@ -83,49 +74,48 @@
 FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, EnsureMetadataMounted);
 FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, GetSnapshotMergeStatsInstance);
 
-#define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ReturnType, ...)                                  \
-    FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, ReturnType, ISnapshotManager* snapshot, \
-                  ##__VA_ARGS__)
+#define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ...) \
+    FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, snapshot, ##__VA_ARGS__)
 
-SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool, bool wipe) {
-    return snapshot->FinishedSnapshotWrites(wipe);
+SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool wipe) {
+    (void)snapshot->FinishedSnapshotWrites(wipe);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, bool, const ProcessUpdateStateArgs& args) {
+SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, const ProcessUpdateStateArgs& args) {
     std::function<bool()> before_cancel;
     if (args.has_before_cancel()) {
         before_cancel = [&]() { return args.fail_before_cancel(); };
     }
-    return snapshot->ProcessUpdateState({}, before_cancel);
+    (void)snapshot->ProcessUpdateState({}, before_cancel);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, UpdateState, bool has_progress_arg) {
+SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, bool has_progress_arg) {
     double progress;
-    return snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr);
+    (void)snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool, bool has_callback) {
+SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool has_callback) {
     std::function<void()> callback;
     if (has_callback) {
         callback = []() {};
     }
-    return snapshot->HandleImminentDataWipe(callback);
+    (void)snapshot->HandleImminentDataWipe(callback);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(Dump, bool) {
+SNAPSHOT_FUZZ_FUNCTION(Dump) {
     std::stringstream ss;
-    return snapshot->Dump(ss);
+    (void)snapshot->Dump(ss);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, bool, const DeltaArchiveManifest& manifest) {
-    return snapshot->CreateUpdateSnapshots(manifest);
+SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, const DeltaArchiveManifest& manifest) {
+    (void)snapshot->CreateUpdateSnapshots(manifest);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, bool, const std::string& name) {
-    return snapshot->UnmapUpdateSnapshot(name);
+SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, const std::string& name) {
+    (void)snapshot->UnmapUpdateSnapshot(name);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions, bool,
+SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions,
                        const CreateLogicalAndSnapshotPartitionsArgs& args) {
     const std::string* super;
     if (args.use_correct_super()) {
@@ -133,21 +123,20 @@
     } else {
         super = &args.super();
     }
-    return snapshot->CreateLogicalAndSnapshotPartitions(
+    (void)snapshot->CreateLogicalAndSnapshotPartitions(
             *super, std::chrono::milliseconds(args.timeout_millis()));
 }
 
-SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata, CreateResult,
+SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata,
                        const RecoveryCreateSnapshotDevicesArgs& args) {
     std::unique_ptr<AutoDevice> device;
     if (args.has_metadata_device_object()) {
         device = std::make_unique<DummyAutoDevice>(args.metadata_mounted());
     }
-    return snapshot->RecoveryCreateSnapshotDevices(device);
+    (void)snapshot->RecoveryCreateSnapshotDevices(device);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, bool,
-                       const CreateLogicalPartitionParamsProto& params_proto) {
+SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, const CreateLogicalPartitionParamsProto& params_proto) {
     auto partition_opener = std::make_unique<TestPartitionOpener>(GetSnapshotFuzzEnv()->super());
     CreateLogicalPartitionParams params;
     if (params_proto.use_correct_super()) {
@@ -164,10 +153,10 @@
     params.device_name = params_proto.device_name();
     params.partition_opener = partition_opener.get();
     std::string path;
-    return snapshot->MapUpdateSnapshot(params, &path);
+    (void)snapshot->MapUpdateSnapshot(params, &path);
 }
 
-SNAPSHOT_FUZZ_FUNCTION(SwitchSlot, void) {
+SNAPSHOT_FUZZ_FUNCTION(SwitchSlot) {
     (void)snapshot;
     CHECK(current_module != nullptr);
     CHECK(current_module->device_info != nullptr);
@@ -205,8 +194,7 @@
 }
 // Stop logging (except fatal messages) after global initialization. This is only done once.
 int StopLoggingAfterGlobalInit() {
-    (void)GetSnapshotFuzzEnv();
-    [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silencer;
+    [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silincer;
     SetLogger(&FatalOnlyLogger);
     return 0;
 }
@@ -214,10 +202,15 @@
 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) {
+}  // namespace android::snapshot
+
+DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) {
+    using namespace android::snapshot;
+
     current_data = &snapshot_fuzz_data;
 
     auto env = GetSnapshotFuzzEnv();
@@ -226,127 +219,9 @@
     auto test_module = env->CheckCreateSnapshotManager(snapshot_fuzz_data);
     current_module = &test_module;
     CHECK(test_module.snapshot);
-    return test_module;
-}
 
-void TearDownTest() {
+    SnapshotManagerAction::ExecuteAll(test_module.snapshot.get(), snapshot_fuzz_data.actions());
+
     current_module = nullptr;
     current_data = nullptr;
 }
-
-}  // namespace android::snapshot
-
-DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) {
-    using namespace android::snapshot;
-
-    [[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/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/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/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/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 601904a..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.
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 ee86566..d203062 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -32,10 +32,10 @@
     name: "liblogd",
 
     srcs: [
-        "LogCommand.cpp",
         "ChattyLogBuffer.cpp",
         "CommandListener.cpp",
         "LogListener.cpp",
+        "LogPermissions.cpp",
         "LogReader.cpp",
         "LogReaderList.cpp",
         "LogReaderThread.cpp",
diff --git a/logd/ChattyLogBuffer.cpp b/logd/ChattyLogBuffer.cpp
index fa5bcee..c6c9a7c 100644
--- a/logd/ChattyLogBuffer.cpp
+++ b/logd/ChattyLogBuffer.cpp
@@ -31,11 +31,8 @@
 #include <unordered_map>
 #include <utility>
 
-#include <cutils/properties.h>
 #include <private/android_logger.h>
 
-#include "LogKlog.h"
-#include "LogReader.h"
 #include "LogUtils.h"
 
 #ifndef __predict_false
@@ -494,8 +491,8 @@
     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 ChattyLogBuffer::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->deadline().time_since_epoch().count() != 0) {
         // Allow a blocked WRAP deadline reader to trigger and start reporting the log data.
@@ -503,8 +500,8 @@
     } else {
         // tell slow reader to skip entries to catch up
         android::prdebug(
-                "Skipping %lu entries from slow reader, pid %d, from ChattyLogBuffer::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);
     }
 }
@@ -872,8 +869,8 @@
                 for (const auto& reader_thread : reader_list_->reader_threads()) {
                     if (reader_thread->IsWatching(id)) {
                         android::prdebug(
-                                "Kicking blocked reader, pid %d, from ChattyLogBuffer::clear()\n",
-                                reader_thread->client()->getPid());
+                                "Kicking blocked reader, %s, from ChattyLogBuffer::clear()\n",
+                                reader_thread->name().c_str());
                         reader_thread->release_Locked();
                     }
                 }
@@ -911,10 +908,10 @@
 }
 
 uint64_t ChattyLogBuffer::FlushTo(
-        SocketClient* reader, uint64_t start, pid_t* lastTid, bool privileged, bool security,
+        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();
 
@@ -940,11 +937,11 @@
     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;
         }
 
@@ -973,11 +970,10 @@
 
         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
index d9cd24f..29a421d 100644
--- a/logd/ChattyLogBuffer.h
+++ b/logd/ChattyLogBuffer.h
@@ -28,15 +28,15 @@
 
 #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 LogReaderList;
-class LogReaderThread;
-
 class ChattyLogBuffer : public LogBuffer {
     LogBufferElementCollection mLogElements;
     pthread_rwlock_t mLogElementsLock;
@@ -63,7 +63,7 @@
     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(
-            SocketClient* writer, uint64_t start, pid_t* lastTid, bool privileged, bool security,
+            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;
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 0ff19f8..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)
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/LogBuffer.h b/logd/LogBuffer.h
index 887e5f0..6274051 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -25,6 +25,8 @@
 
 #include "LogBufferElement.h"
 
+class LogWriter;
+
 enum class FlushToResult {
     kSkip,
     kStop,
@@ -42,10 +44,10 @@
     // 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)
+    static const uint64_t FLUSH_ERROR = 0;
     virtual uint64_t FlushTo(
-            SocketClient* writer, uint64_t start,
+            LogWriter* writer, uint64_t start,
             pid_t* last_tid,  // nullable
-            bool privileged, bool security,
             const std::function<FlushToResult(const LogBufferElement* element)>& filter) = 0;
 
     virtual bool Clear(log_id_t id, uid_t uid) = 0;
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 32f641b..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 "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 3d0b65e..2f2d70d 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -22,7 +22,8 @@
 #include <sys/types.h>
 
 #include <log/log.h>
-#include <sysutils/SocketClient.h>
+
+#include "LogWriter.h"
 
 class LogStatistics;
 
@@ -94,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.h b/logd/LogKlog.h
index 77b24bc..56e0452 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -19,10 +19,9 @@
 #include <private/android_logger.h>
 #include <sysutils/SocketListener.h>
 
+#include "LogBuffer.h"
 #include "LogStatistics.h"
 
-class LogBuffer;
-
 class LogKlog : public SocketListener {
     LogBuffer* logbuf;
     const log_time signature;
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index d2e2efa..a6ab50b 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -30,7 +30,7 @@
 
 #include "LogBuffer.h"
 #include "LogListener.h"
-#include "LogUtils.h"
+#include "LogPermissions.h"
 
 LogListener::LogListener(LogBuffer* buf) : socket_(GetLogSocket()), logbuf_(buf) {}
 
diff --git a/logd/LogListener.h b/logd/LogListener.h
index d468df8..c114e38 100644
--- a/logd/LogListener.h
+++ b/logd/LogListener.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include "LogBuffer.h"
-#include "LogReader.h"
 
 class LogListener {
   public:
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 6f91372..89562a4 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -23,19 +23,58 @@
 
 #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;
 }
 
+static std::string SocketClientToName(SocketClient* client) {
+    return android::base::StringPrintf("pid %d, fd %d", client->getPid(), client->getSocket());
+}
+
+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) {}
+
+    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;
+    }
+
+    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) {}
 
@@ -51,21 +90,14 @@
 
     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.
-    {
-        auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
-        for (const auto& entry : reader_list_->reader_threads()) {
-            if (entry->client() == cli) {
-                entry->release_Locked();
-                return false;
-            }
-        }
+    // Clients are only allowed to send one command, disconnect them if they send another.
+    if (DoSocketDelete(cli)) {
+        return false;
     }
 
     unsigned long tail = 0;
@@ -131,6 +163,9 @@
     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) {
@@ -159,11 +194,10 @@
             return FlushToResult::kSkip;
         };
 
-        log_buffer_->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();
@@ -181,13 +215,9 @@
     }
 
     auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
-    auto entry = std::make_unique<LogReaderThread>(*this, *reader_list_, cli, nonBlock, tail,
-                                                   logMask, pid, start, sequence, deadline,
-                                                   privileged, can_read_security);
-    if (!entry->startReader_Locked()) {
-        return false;
-    }
-
+    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();
     reader_list_->reader_threads().emplace_front(std::move(entry));
@@ -200,17 +230,16 @@
     return true;
 }
 
-void LogReader::doSocketDelete(SocketClient* cli) {
+bool LogReader::DoSocketDelete(SocketClient* cli) {
+    auto cli_name = SocketClientToName(cli);
     auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
-    auto it = reader_list_->reader_threads().begin();
-    while (it != reader_list_->reader_threads().end()) {
-        LogReaderThread* entry = it->get();
-        if (entry->client() == cli) {
-            entry->release_Locked();
-            break;
+    for (const auto& reader : reader_list_->reader_threads()) {
+        if (reader->name() == cli_name) {
+            reader->release_Locked();
+            return true;
         }
-        it++;
     }
+    return false;
 }
 
 int LogReader::getLogSocket() {
diff --git a/logd/LogReader.h b/logd/LogReader.h
index 7df3f6b..b85a584 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -18,26 +18,23 @@
 
 #include <sysutils/SocketListener.h>
 
+#include "LogBuffer.h"
 #include "LogReaderList.h"
 #include "LogReaderThread.h"
 
 #define LOGD_SNDTIMEO 32
 
-class LogBuffer;
-
 class LogReader : public SocketListener {
   public:
     explicit LogReader(LogBuffer* logbuf, LogReaderList* reader_list);
 
-    LogBuffer* log_buffer() const { return log_buffer_; }
-
   protected:
     virtual bool onDataAvailable(SocketClient* cli);
 
   private:
     static int getLogSocket();
 
-    void doSocketDelete(SocketClient* cli);
+    bool DoSocketDelete(SocketClient* cli);
 
     LogBuffer* log_buffer_;
     LogReaderList* reader_list_;
diff --git a/logd/LogReaderThread.cpp b/logd/LogReaderThread.cpp
index e58e3eb..b2001b5 100644
--- a/logd/LogReaderThread.cpp
+++ b/logd/LogReaderThread.cpp
@@ -23,50 +23,40 @@
 #include <thread>
 
 #include "LogBuffer.h"
-#include "LogReader.h"
+#include "LogReaderList.h"
 
 using namespace std::placeholders;
 
-LogReaderThread::LogReaderThread(LogReader& reader, LogReaderList& reader_list,
-                                 SocketClient* client, bool non_block, unsigned long tail,
-                                 unsigned int log_mask, pid_t pid, log_time start_time,
-                                 uint64_t start, std::chrono::steady_clock::time_point deadline,
-                                 bool privileged, bool can_read_security_logs)
-    : leading_dropped_(false),
-      reader_(reader),
+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,
+                                 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),
       deadline_(deadline),
-      non_block_(non_block),
-      privileged_(privileged),
-      can_read_security_logs_(can_read_security_logs) {
+      non_block_(non_block) {
     memset(last_tid_, 0, sizeof(last_tid_));
     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_.log_buffer();
-
     leading_dropped_ = true;
 
-    auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+    auto lock = std::unique_lock{reader_list_->reader_threads_lock()};
 
     uint64_t start = start_;
 
@@ -84,14 +74,14 @@
         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
@@ -104,7 +94,7 @@
 
         lock.lock();
 
-        if (start == LogBufferElement::FLUSH_ERROR) {
+        if (start == LogBuffer::FLUSH_ERROR) {
             break;
         }
 
@@ -121,10 +111,9 @@
         }
     }
 
-    reader_.release(client);
-    client->decRef();
+    writer_->Release();
 
-    auto& log_reader_threads = reader_list_.reader_threads();
+    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; });
 
@@ -135,7 +124,7 @@
 
 // A first pass to count the number of elements
 FlushToResult LogReaderThread::FilterFirstPass(const LogBufferElement* element) {
-    auto lock = std::lock_guard{reader_list_.reader_threads_lock()};
+    auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
 
     if (leading_dropped_) {
         if (element->getDropped()) {
@@ -158,7 +147,7 @@
 
 // A second pass to send the selected elements
 FlushToResult LogReaderThread::FilterSecondPass(const LogBufferElement* element) {
-    auto lock = std::lock_guard{reader_list_.reader_threads_lock()};
+    auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
 
     start_ = element->getSequence();
 
diff --git a/logd/LogReaderThread.h b/logd/LogReaderThread.h
index f828b6e..e48a3ca 100644
--- a/logd/LogReaderThread.h
+++ b/logd/LogReaderThread.h
@@ -30,21 +30,17 @@
 #include <sysutils/SocketClient.h>
 
 #include "LogBuffer.h"
+#include "LogBufferElement.h"
+#include "LogWriter.h"
 
-class LogReader;
-class LogBufferElement;
 class LogReaderList;
 
 class LogReaderThread {
   public:
-    LogReaderThread(LogReader& reader, LogReaderList& reader_list, SocketClient* client,
-                    bool non_block, unsigned long tail, unsigned int log_mask, pid_t pid,
-                    log_time start_time, uint64_t sequence,
-                    std::chrono::steady_clock::time_point deadline, bool privileged,
-                    bool can_read_security_logs);
-
-    bool startReader_Locked();
-
+    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,
+                    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; }
@@ -52,7 +48,7 @@
 
     void release_Locked() {
         // gracefully shut down the socket.
-        shutdown(client_->getSocket(), SHUT_RDWR);
+        writer_->Shutdown();
         release_ = true;
         thread_triggered_condition_.notify_all();
     }
@@ -60,7 +56,7 @@
     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_; }
     std::chrono::steady_clock::time_point deadline() const { return deadline_; }
 
@@ -70,19 +66,17 @@
     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;
     // Indicates whether or not 'leading' (first logs seen starting from start_) 'dropped' (chatty)
     // messages should be ignored.
     bool leading_dropped_;
 
-    // Condition variable for waking the reader thread if there are messages pending for its client.
-    std::condition_variable thread_triggered_condition_;
-
-    // Reference to the parent thread that manages log reader sockets.
-    LogReader& reader_;
-    // Reference to the parent list that shares its lock with each instance
-    LogReaderList& reader_list_;
     // 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.
@@ -105,8 +99,6 @@
     // 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_;
@@ -117,10 +109,4 @@
     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_;
 };
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/main.cpp b/logd/main.cpp
index 6e1144b..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>
@@ -53,6 +52,7 @@
 #include "LogBuffer.h"
 #include "LogKlog.h"
 #include "LogListener.h"
+#include "LogReader.h"
 #include "LogStatistics.h"
 #include "LogTags.h"
 #include "LogUtils.h"