Merge changes Id8d9fa6c,I47d3ad50,I7cebbf75,Id10e320a

* changes:
  adbd: avoid compiling more code in the daemon.
  adb: don't run all of the tests again over TCP in coverage.
  adbd: respect ADB_TRACE on host adbd.
  adb: mark kMaxProcessNameLength as constexpr.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+  license_type: NOTICE
+}
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index c8dbf77..dcf92be 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -3,3 +3,6 @@
 
 [Builtin Hooks Options]
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+
+[Hook Scripts]
+aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "."
diff --git a/adb/apex/Android.bp b/adb/apex/Android.bp
index 4346f67..ddb17da 100644
--- a/adb/apex/Android.bp
+++ b/adb/apex/Android.bp
@@ -1,6 +1,7 @@
 apex_defaults {
     name: "com.android.adbd-defaults",
     updatable: true,
+    min_sdk_version: "R",
 
     binaries: ["adbd"],
     compile_multilib: "both",
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 59c8563..e562f8b 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -154,6 +154,14 @@
     *buf = '\0';
 }
 
+static unique_fd send_command(const std::vector<std::string>& cmd_args, std::string* error) {
+    if (is_abb_exec_supported()) {
+        return send_abb_exec_command(cmd_args, error);
+    } else {
+        return unique_fd(adb_connect(android::base::Join(cmd_args, " "), error));
+    }
+}
+
 static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy) {
     printf("Performing Streamed Install\n");
 
@@ -226,12 +234,7 @@
         cmd_args.push_back("--apex");
     }
 
-    unique_fd remote_fd;
-    if (use_abb_exec) {
-        remote_fd = send_abb_exec_command(cmd_args, &error);
-    } else {
-        remote_fd.reset(adb_connect(android::base::Join(cmd_args, " "), &error));
-    }
+    unique_fd remote_fd = send_command(cmd_args, &error);
     if (remote_fd < 0) {
         fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
         return 1;
@@ -547,24 +550,28 @@
 
     if (first_apk == -1) error_exit("need APK file on command line");
 
-    std::string install_cmd;
-    if (best_install_mode() == INSTALL_PUSH) {
-        install_cmd = "exec:pm";
-    } else {
-        install_cmd = "exec:cmd package";
-    }
+    const bool use_abb_exec = is_abb_exec_supported();
 
-    std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64,
-                                                  install_cmd.c_str(), total_size);
+    const std::string install_cmd =
+            use_abb_exec ? "package"
+                         : best_install_mode() == INSTALL_PUSH ? "exec:pm" : "exec:cmd package";
+
+    std::vector<std::string> cmd_args = {install_cmd, "install-create", "-S",
+                                         std::to_string(total_size)};
+    cmd_args.reserve(first_apk + 4);
     for (int i = 1; i < first_apk; i++) {
-        cmd += " " + escape_arg(argv[i]);
+        if (use_abb_exec) {
+            cmd_args.push_back(argv[i]);
+        } else {
+            cmd_args.push_back(escape_arg(argv[i]));
+        }
     }
 
     // Create install session
     std::string error;
     char buf[BUFSIZ];
     {
-        unique_fd fd(adb_connect(cmd, &error));
+        unique_fd fd = send_command(cmd_args, &error);
         if (fd < 0) {
             fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
             return EXIT_FAILURE;
@@ -586,6 +593,7 @@
         fputs(buf, stderr);
         return EXIT_FAILURE;
     }
+    const auto session_id_str = std::to_string(session_id);
 
     // Valid session, now stream the APKs
     bool success = true;
@@ -598,10 +606,15 @@
             goto finalize_session;
         }
 
-        std::string cmd =
-                android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %s -",
-                                            install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
-                                            session_id, android::base::Basename(file).c_str());
+        std::vector<std::string> cmd_args = {
+                install_cmd,
+                "install-write",
+                "-S",
+                std::to_string(sb.st_size),
+                session_id_str,
+                android::base::Basename(file),
+                "-",
+        };
 
         unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
         if (local_fd < 0) {
@@ -611,7 +624,7 @@
         }
 
         std::string error;
-        unique_fd remote_fd(adb_connect(cmd, &error));
+        unique_fd remote_fd = send_command(cmd_args, &error);
         if (remote_fd < 0) {
             fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
             success = false;
@@ -636,10 +649,13 @@
 
 finalize_session:
     // Commit session if we streamed everything okay; otherwise abandon.
-    std::string service = android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
-                                                      success ? "commit" : "abandon", session_id);
+    std::vector<std::string> service_args = {
+            install_cmd,
+            success ? "install-commit" : "install-abandon",
+            session_id_str,
+    };
     {
-        unique_fd fd(adb_connect(service, &error));
+        unique_fd fd = send_command(service_args, &error);
         if (fd < 0) {
             fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
             return EXIT_FAILURE;
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index d565e01..efb6c2f 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -240,6 +240,7 @@
         " $ANDROID_SERIAL          serial number to connect to (see -s)\n"
         " $ANDROID_LOG_TAGS        tags to be used by logcat (see logcat --help)\n"
         " $ADB_LOCAL_TRANSPORT_MAX_PORT max emulator scan port (default 5585, 16 emus)\n"
+        " $ADB_MDNS_AUTO_CONNECT   comma-separated list of mdns services to allow auto-connect (default adb-tls-connect)\n"
     );
     // clang-format on
 }
@@ -1711,14 +1712,21 @@
         }
         printf("List of devices attached\n");
         return adb_query_command(query);
-    }
-    else if (!strcmp(argv[0], "connect")) {
+    } else if (!strcmp(argv[0], "transport-id")) {
+        TransportId transport_id;
+        std::string error;
+        unique_fd fd(adb_connect(&transport_id, "host:features", &error, true));
+        if (fd == -1) {
+            error_exit("%s", error.c_str());
+        }
+        printf("%" PRIu64 "\n", transport_id);
+        return 0;
+    } else if (!strcmp(argv[0], "connect")) {
         if (argc != 2) error_exit("usage: adb connect HOST[:PORT]");
 
         std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
         return adb_query_command(query);
-    }
-    else if (!strcmp(argv[0], "disconnect")) {
+    } else if (!strcmp(argv[0], "disconnect")) {
         if (argc > 2) error_exit("usage: adb disconnect [HOST[:PORT]]");
 
         std::string query = android::base::StringPrintf("host:disconnect:%s",
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 2bf062f..c9993b7 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -26,6 +26,7 @@
 
 #include <memory>
 #include <thread>
+#include <unordered_set>
 #include <vector>
 
 #include <android-base/stringprintf.h>
@@ -42,27 +43,75 @@
 
 static DNSServiceRef service_refs[kNumADBDNSServices];
 static fdevent* service_ref_fdes[kNumADBDNSServices];
+static auto& g_autoconn_whitelist = *new std::unordered_set<int>();
 
-static int adb_DNSServiceIndexByName(const char* regType) {
+static int adb_DNSServiceIndexByName(std::string_view regType) {
     for (int i = 0; i < kNumADBDNSServices; ++i) {
-        if (!strncmp(regType, kADBDNSServices[i], strlen(kADBDNSServices[i]))) {
+        if (!strncmp(regType.data(), kADBDNSServices[i], strlen(kADBDNSServices[i]))) {
             return i;
         }
     }
     return -1;
 }
 
-static bool adb_DNSServiceShouldConnect(const char* regType, const char* serviceName) {
-    int index = adb_DNSServiceIndexByName(regType);
-    if (index == kADBTransportServiceRefIndex) {
-        // Ignore adb-EMULATOR* service names, as it interferes with the
-        // emulator ports that are already connected.
-        if (android::base::StartsWith(serviceName, "adb-EMULATOR")) {
-            LOG(INFO) << "Ignoring emulator transport service [" << serviceName << "]";
-            return false;
+static void config_auto_connect_services() {
+    // ADB_MDNS_AUTO_CONNECT is a comma-delimited list of mdns services
+    // that are allowed to auto-connect. By default, only allow "adb-tls-connect"
+    // to auto-connect, since this is filtered down to auto-connect only to paired
+    // devices.
+    g_autoconn_whitelist.insert(kADBSecureConnectServiceRefIndex);
+    const char* srvs = getenv("ADB_MDNS_AUTO_CONNECT");
+    if (!srvs) {
+        return;
+    }
+
+    if (strcmp(srvs, "0") == 0) {
+        D("Disabling all auto-connecting");
+        g_autoconn_whitelist.clear();
+        return;
+    }
+
+    if (strcmp(srvs, "1") == 0) {
+        D("Allow all auto-connecting");
+        g_autoconn_whitelist.insert(kADBTransportServiceRefIndex);
+        return;
+    }
+
+    // Selectively choose which services to allow auto-connect.
+    // E.g. ADB_MDNS_AUTO_CONNECT=adb,adb-tls-connect would allow
+    // _adb._tcp and _adb-tls-connnect._tcp services to auto-connect.
+    auto srvs_list = android::base::Split(srvs, ",");
+    std::unordered_set<int> new_whitelist;
+    for (const auto& item : srvs_list) {
+        auto full_srv = android::base::StringPrintf("_%s._tcp", item.data());
+        int idx = adb_DNSServiceIndexByName(full_srv);
+        if (idx >= 0) {
+            new_whitelist.insert(idx);
         }
     }
-    return (index == kADBTransportServiceRefIndex || index == kADBSecureConnectServiceRefIndex);
+
+    if (!new_whitelist.empty()) {
+        g_autoconn_whitelist = std::move(new_whitelist);
+    }
+}
+
+static bool adb_DNSServiceShouldAutoConnect(const char* regType, const char* serviceName) {
+    // Try to auto-connect to any "_adb" or "_adb-tls-connect" services excluding emulator services.
+    int index = adb_DNSServiceIndexByName(regType);
+    if (index != kADBTransportServiceRefIndex && index != kADBSecureConnectServiceRefIndex) {
+        return false;
+    }
+    if (g_autoconn_whitelist.find(index) == g_autoconn_whitelist.end()) {
+        D("Auto-connect for regType '%s' disabled", regType);
+        return false;
+    }
+    // Ignore adb-EMULATOR* service names, as it interferes with the
+    // emulator ports that are already connected.
+    if (android::base::StartsWith(serviceName, "adb-EMULATOR")) {
+        LOG(INFO) << "Ignoring emulator transport service [" << serviceName << "]";
+        return false;
+    }
+    return true;
 }
 
 // Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD()
@@ -95,7 +144,7 @@
         return initialized_;
     }
 
-    virtual ~AsyncServiceRef() {
+    void DestroyServiceRef() {
         if (!initialized_) {
             return;
         }
@@ -103,9 +152,13 @@
         // Order matters here! Must destroy the fdevent first since it has a
         // reference to |sdRef_|.
         fdevent_destroy(fde_);
+        D("DNSServiceRefDeallocate(sdRef=%p)", sdRef_);
         DNSServiceRefDeallocate(sdRef_);
+        initialized_ = false;
     }
 
+    virtual ~AsyncServiceRef() { DestroyServiceRef(); }
+
   protected:
     DNSServiceRef sdRef_;
 
@@ -154,6 +207,7 @@
         if (ret != kDNSServiceErr_NoError) {
             D("Got %d from DNSServiceGetAddrInfo.", ret);
         } else {
+            D("DNSServiceGetAddrInfo(sdRef=%p, hosttarget=%s)", sdRef_, hosttarget);
             Initialize();
         }
 
@@ -174,7 +228,7 @@
         return true;
     }
 
-    void Connect(const sockaddr* address) {
+    bool AddToServiceRegistry(const sockaddr* address) {
         sa_family_ = address->sa_family;
 
         if (sa_family_ == AF_INET) {
@@ -185,18 +239,18 @@
             addr_format_ = "[%s]:%hu";
         } else {  // Should be impossible
             D("mDNS resolved non-IP address.");
-            return;
+            return false;
         }
 
         // Winsock version requires the const cast Because Microsoft.
         if (!inet_ntop(sa_family_, const_cast<void*>(ip_addr_data_), ip_addr_, sizeof(ip_addr_))) {
             D("Could not convert IP address to string.");
-            return;
+            return false;
         }
 
         // adb secure service needs to do something different from just
         // connecting here.
-        if (adb_DNSServiceShouldConnect(regType_.c_str(), serviceName_.c_str())) {
+        if (adb_DNSServiceShouldAutoConnect(regType_.c_str(), serviceName_.c_str())) {
             std::string response;
             D("Attempting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)", serviceName_.c_str(),
               regType_.c_str(), ip_addr_, port_);
@@ -215,19 +269,32 @@
         }
 
         int adbSecureServiceType = serviceIndex();
+        ServiceRegistry* services = nullptr;
         switch (adbSecureServiceType) {
             case kADBTransportServiceRefIndex:
-                sAdbTransportServices->push_back(this);
+                services = sAdbTransportServices;
                 break;
             case kADBSecurePairingServiceRefIndex:
-                sAdbSecurePairingServices->push_back(this);
+                services = sAdbSecurePairingServices;
                 break;
             case kADBSecureConnectServiceRefIndex:
-                sAdbSecureConnectServices->push_back(this);
+                services = sAdbSecureConnectServices;
                 break;
             default:
-                break;
+                LOG(WARNING) << "No registry available for reg_type=[" << regType_ << "]";
+                return false;
         }
+
+        if (!services->empty()) {
+            // Remove the previous resolved service, if any.
+            services->erase(std::remove_if(services->begin(), services->end(),
+                                           [&](std::unique_ptr<ResolvedService>& service) {
+                                               return (serviceName_ == service->serviceName());
+                                           }));
+        }
+        services->push_back(std::unique_ptr<ResolvedService>(this));
+
+        return true;
     }
 
     int serviceIndex() const { return adb_DNSServiceIndexByName(regType_.c_str()); }
@@ -242,7 +309,7 @@
 
     uint16_t port() const { return port_; }
 
-    using ServiceRegistry = std::vector<ResolvedService*>;
+    using ServiceRegistry = std::vector<std::unique_ptr<ResolvedService>>;
 
     // unencrypted tcp connections
     static ServiceRegistry* sAdbTransportServices;
@@ -272,13 +339,13 @@
 };
 
 // static
-std::vector<ResolvedService*>* ResolvedService::sAdbTransportServices = NULL;
+ResolvedService::ServiceRegistry* ResolvedService::sAdbTransportServices = NULL;
 
 // static
-std::vector<ResolvedService*>* ResolvedService::sAdbSecurePairingServices = NULL;
+ResolvedService::ServiceRegistry* ResolvedService::sAdbSecurePairingServices = NULL;
 
 // static
-std::vector<ResolvedService*>* ResolvedService::sAdbSecureConnectServices = NULL;
+ResolvedService::ServiceRegistry* ResolvedService::sAdbSecureConnectServices = NULL;
 
 // static
 void ResolvedService::initAdbServiceRegistries() {
@@ -299,7 +366,7 @@
                                      adb_secure_foreach_service_callback cb) {
     initAdbServiceRegistries();
 
-    for (auto service : services) {
+    for (const auto& service : services) {
         auto service_name = service->serviceName();
         auto reg_type = service->regType();
         auto ip = service->ipAddress();
@@ -317,7 +384,7 @@
 bool ResolvedService::connectByServiceName(const ServiceRegistry& services,
                                            const std::string& service_name) {
     initAdbServiceRegistries();
-    for (auto service : services) {
+    for (const auto& service : services) {
         if (service_name == service->serviceName()) {
             D("Got service_name match [%s]", service->serviceName().c_str());
             return service->ConnectSecureWifiDevice();
@@ -344,23 +411,28 @@
                                                  service_name);
 }
 
-static void DNSSD_API register_service_ip(DNSServiceRef /*sdRef*/,
-                                          DNSServiceFlags /*flags*/,
+static void DNSSD_API register_service_ip(DNSServiceRef sdRef, DNSServiceFlags flags,
                                           uint32_t /*interfaceIndex*/,
-                                          DNSServiceErrorType /*errorCode*/,
-                                          const char* /*hostname*/,
-                                          const sockaddr* address,
-                                          uint32_t /*ttl*/,
-                                          void* context) {
-    D("Got IP for service.");
+                                          DNSServiceErrorType errorCode, const char* hostname,
+                                          const sockaddr* address, uint32_t ttl, void* context) {
+    D("%s: sdRef=%p flags=0x%08x errorCode=%u ttl=%u", __func__, sdRef, flags, errorCode, ttl);
     std::unique_ptr<ResolvedService> data(
         reinterpret_cast<ResolvedService*>(context));
-    data->Connect(address);
+    // Only resolve the address once. If the address or port changes, we'll just get another
+    // registration.
+    data->DestroyServiceRef();
 
-    // For ADB Secure services, keep those ResolvedService's around
-    // for later processing with secure connection establishment.
-    if (data->serviceIndex() != kADBTransportServiceRefIndex) {
-        data.release();
+    if (errorCode != kDNSServiceErr_NoError) {
+        D("Got error while looking up ipaddr [%u]", errorCode);
+        return;
+    }
+
+    if (flags & kDNSServiceFlagsAdd) {
+        D("Resolved IP address for [%s]. Adding to service registry.", hostname);
+        auto* ptr = data.release();
+        if (!ptr->AddToServiceRegistry(address)) {
+            data.reset(ptr);
+        }
     }
 }
 
@@ -410,6 +482,7 @@
 };
 
 static void adb_RemoveDNSService(const char* regType, const char* serviceName) {
+    D("%s: regType=[%s] serviceName=[%s]", __func__, regType, serviceName);
     int index = adb_DNSServiceIndexByName(regType);
     ResolvedService::ServiceRegistry* services;
     switch (index) {
@@ -426,10 +499,15 @@
             return;
     }
 
+    if (services->empty()) {
+        return;
+    }
+
     std::string sName(serviceName);
-    services->erase(std::remove_if(
-            services->begin(), services->end(),
-            [&sName](ResolvedService* service) { return (sName == service->serviceName()); }));
+    services->erase(std::remove_if(services->begin(), services->end(),
+                                   [&sName](std::unique_ptr<ResolvedService>& service) {
+                                       return (sName == service->serviceName());
+                                   }));
 }
 
 // Returns the version the device wanted to advertise,
@@ -539,8 +617,15 @@
 }
 
 void init_mdns_transport_discovery_thread(void) {
-    int errorCodes[kNumADBDNSServices];
+    config_auto_connect_services();
+    std::string res;
+    std::for_each(g_autoconn_whitelist.begin(), g_autoconn_whitelist.end(), [&](const int& i) {
+        res += kADBDNSServices[i];
+        res += ",";
+    });
+    D("mdns auto-connect whitelist: [%s]", res.data());
 
+    int errorCodes[kNumADBDNSServices];
     for (int i = 0; i < kNumADBDNSServices; ++i) {
         errorCodes[i] = DNSServiceBrowse(&service_refs[i], 0, 0, kADBDNSServices[i], nullptr,
                                          on_service_browsed, nullptr);
diff --git a/adb/coverage/gen_coverage.sh b/adb/coverage/gen_coverage.sh
index ace798f..43d45f0 100755
--- a/adb/coverage/gen_coverage.sh
+++ b/adb/coverage/gen_coverage.sh
@@ -17,10 +17,10 @@
 
 # Check that we can connect to it.
 adb disconnect
-adb tcpip $REMOTE_PORT
 
-# TODO: Add `adb transport-id` and wait-for-offline on it.
-sleep 5
+TRANSPORT_ID=$(adb transport-id)
+adb tcpip $REMOTE_PORT
+adb -t $TRANSPORT_ID wait-for-disconnect
 
 adb connect $REMOTE
 
@@ -32,13 +32,16 @@
 fi
 
 # Back to USB, and make sure adbd is root.
+adb -s $REMOTE usb
 adb disconnect $REMOTE
 
+adb wait-for-device root
 adb root
-adb wait-for-device usb
+adb wait-for-device
 
-# TODO: Add `adb transport-id` and wait-for-offline on it.
-sleep 5
+TRANSPORT_ID=$(adb transport-id)
+adb usb
+adb -t $TRANSPORT_ID wait-for-disconnect
 
 adb wait-for-device
 
@@ -61,10 +64,9 @@
 adb shell setprop persist.adb.trace_mask 1
 
 ### Run test_device.py over USB.
+TRANSPORT_ID=$(adb transport-id)
 adb shell killall adbd
-
-# TODO: Add `adb transport-id` and wait-for-offline on it.
-sleep 5
+adb -t $TRANSPORT_ID wait-for-disconnect
 
 adb wait-for-device shell rm -rf "/data/misc/trace/*" /data/local/tmp/adb_coverage/
 "$OUTPUT_DIR"/../test_device.py
@@ -80,8 +82,10 @@
 sleep 5
 
 # Restart adbd in tcp mode.
+TRANSPORT_ID=$(adb transport-id)
 adb tcpip $REMOTE_PORT
-sleep 5
+adb -t $TRANSPORT_ID wait-for-disconnect
+
 adb connect $REMOTE
 adb -s $REMOTE wait-for-device
 
diff --git a/adb/fastdeploy/proto/ApkEntry.proto b/adb/fastdeploy/proto/ApkEntry.proto
index d84c5a5..ed5056e 100644
--- a/adb/fastdeploy/proto/ApkEntry.proto
+++ b/adb/fastdeploy/proto/ApkEntry.proto
@@ -5,7 +5,6 @@
 option java_package = "com.android.fastdeploy";
 option java_outer_classname = "ApkEntryProto";
 option java_multiple_files = true;
-option optimize_for = LITE_RUNTIME;
 
 message APKDump {
     string name = 1;
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 03bdcbd..9912f11 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -659,14 +659,14 @@
                 print(f"Registering {serv_instance}.{serv_type} ...")
                 with zeroconf_register_service(zc, service_info) as info:
                     """Give adb some time to register the service"""
-                    time.sleep(0.25)
+                    time.sleep(1)
                     print(f"services={_mdns_services(server_port)}")
                     self.assertTrue(any((serv_instance in line and serv_type in line)
                         for line in _mdns_services(server_port)))
 
                 """Give adb some time to unregister the service"""
                 print("Unregistering mdns service...")
-                time.sleep(0.25)
+                time.sleep(1)
                 print(f"services={_mdns_services(server_port)}")
                 self.assertFalse(any((serv_instance in line and serv_type in line)
                     for line in _mdns_services(server_port)))
diff --git a/base/Android.bp b/base/Android.bp
index 894ad6c..61fbc3d 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -51,6 +51,7 @@
         "//apex_available:anyapex",
         "//apex_available:platform",
     ],
+    min_sdk_version: "29",
 }
 
 cc_defaults {
@@ -132,6 +133,7 @@
         "//apex_available:anyapex",
         "//apex_available:platform",
     ],
+    min_sdk_version: "29",
 }
 
 cc_library_static {
@@ -157,6 +159,7 @@
         "errors_test.cpp",
         "expected_test.cpp",
         "file_test.cpp",
+        "logging_splitters_test.cpp",
         "logging_test.cpp",
         "macros_test.cpp",
         "mapped_file_test.cpp",
diff --git a/base/file.cpp b/base/file.cpp
index 6321fc6..97cc2b2 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -225,7 +225,7 @@
     content->reserve(sb.st_size);
   }
 
-  char buf[BUFSIZ];
+  char buf[BUFSIZ] __attribute__((__uninitialized__));
   ssize_t n;
   while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
     content->append(buf, n);
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
index 9603bb1..9470344 100644
--- a/base/include/android-base/expected.h
+++ b/base/include/android-base/expected.h
@@ -182,7 +182,7 @@
                 !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
                 std::is_convertible_v<U&&, T> /* non-explicit */
                 )>
-  // NOLINTNEXTLINE(google-explicit-constructor)
+  // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
   constexpr expected(U&& v) : var_(std::in_place_index<0>, std::forward<U>(v)) {}
 
   template <class U = T _ENABLE_IF(
@@ -192,6 +192,7 @@
                 !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
                 !std::is_convertible_v<U&&, T> /* explicit */
                 )>
+  // NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
   constexpr explicit expected(U&& v) : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
 
   template<class G = E _ENABLE_IF(
@@ -387,13 +388,9 @@
 
 template<class T1, class E1, class T2, class E2>
 constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y) {
-  if (x.has_value() != y.has_value()) {
-    return false;
-  } else if (!x.has_value()) {
-    return x.error() == y.error();
-  } else {
-    return *x == *y;
-  }
+  if (x.has_value() != y.has_value()) return false;
+  if (!x.has_value()) return x.error() == y.error();
+  return *x == *y;
 }
 
 template<class T1, class E1, class T2, class E2>
@@ -581,35 +578,23 @@
 
 template<class E1, class E2>
 constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y) {
-  if (x.has_value() != y.has_value()) {
-    return false;
-  } else if (!x.has_value()) {
-    return x.error() == y.error();
-  } else {
-    return true;
-  }
+  if (x.has_value() != y.has_value()) return false;
+  if (!x.has_value()) return x.error() == y.error();
+  return true;
 }
 
 template<class T1, class E1, class E2>
 constexpr bool operator==(const expected<T1, E1>& x, const expected<void, E2>& y) {
-  if (x.has_value() != y.has_value()) {
-    return false;
-  } else if (!x.has_value()) {
-    return x.error() == y.error();
-  } else {
-    return false;
-  }
+  if (x.has_value() != y.has_value()) return false;
+  if (!x.has_value()) return x.error() == y.error();
+  return false;
 }
 
 template<class E1, class T2, class E2>
 constexpr bool operator==(const expected<void, E1>& x, const expected<T2, E2>& y) {
-  if (x.has_value() != y.has_value()) {
-    return false;
-  } else if (!x.has_value()) {
-    return x.error() == y.error();
-  } else {
-    return false;
-  }
+  if (x.has_value() != y.has_value()) return false;
+  if (!x.has_value()) return x.error() == y.error();
+  return false;
 }
 
 template<class E>
@@ -623,7 +608,7 @@
                 std::is_constructible_v<E, Err> &&
                 !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, std::in_place_t> &&
                 !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, unexpected>)>
-  // NOLINTNEXTLINE(google-explicit-constructor)
+  // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
   constexpr unexpected(Err&& e) : val_(std::forward<Err>(e)) {}
 
   template<class U, class... Args _ENABLE_IF(
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index accc225..26827fb 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -118,8 +118,10 @@
 
 void SetDefaultTag(const std::string& tag);
 
-// We expose this even though it is the default because a user that wants to
-// override the default log buffer will have to construct this themselves.
+// The LogdLogger sends chunks of up to ~4000 bytes at a time to logd.  It does not prevent other
+// threads from writing to logd between sending each chunk, so other threads may interleave their
+// messages.  If preventing interleaving is required, then a custom logger that takes a lock before
+// calling this logger should be provided.
 class LogdLogger {
  public:
   explicit LogdLogger(LogId default_log_id = android::base::MAIN);
diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h
index 5e65876..56a4f3e 100644
--- a/base/include/android-base/result.h
+++ b/base/include/android-base/result.h
@@ -130,6 +130,7 @@
 
   template <typename T>
   Error& operator<<(T&& t) {
+    // NOLINTNEXTLINE(bugprone-suspicious-semicolon)
     if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
       errno_ = t.code();
       return (*this) << t.message();
diff --git a/base/logging.cpp b/base/logging.cpp
index 3c73fea..5bd21da 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -61,6 +61,7 @@
 #include <android-base/threads.h>
 
 #include "liblog_symbols.h"
+#include "logging_splitters.h"
 
 namespace android {
 namespace base {
@@ -190,11 +191,6 @@
   }
 }
 
-static std::mutex& LoggingLock() {
-  static auto& logging_lock = *new std::mutex();
-  return logging_lock;
-}
-
 static LogFunction& Logger() {
 #ifdef __ANDROID__
   static auto& logger = *new LogFunction(LogdLogger());
@@ -239,8 +235,8 @@
 static LogSeverity gMinimumLogSeverity = INFO;
 
 #if defined(__linux__)
-void KernelLogger(android::base::LogId, android::base::LogSeverity severity,
-                  const char* tag, const char*, unsigned int, const char* msg) {
+static void KernelLogLine(const char* msg, int length, android::base::LogSeverity severity,
+                          const char* tag) {
   // clang-format off
   static constexpr int kLogSeverityToKernelLogLevel[] = {
       [android::base::VERBOSE] = 7,              // KERN_DEBUG (there is no verbose kernel log
@@ -264,8 +260,8 @@
   // The kernel's printk buffer is only 1024 bytes.
   // TODO: should we automatically break up long lines into multiple lines?
   // Or we could log but with something like "..." at the end?
-  char buf[1024];
-  size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %s\n", level, tag, msg);
+  char buf[1024] __attribute__((__uninitialized__));
+  size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %.*s\n", level, tag, length, msg);
   if (size > sizeof(buf)) {
     size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n",
                     level, tag, size);
@@ -276,6 +272,11 @@
   iov[0].iov_len = size;
   TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1));
 }
+
+void KernelLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag,
+                  const char*, unsigned int, const char* full_message) {
+  SplitByLines(full_message, KernelLogLine, severity, tag);
+}
 #endif
 
 void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file, unsigned int line,
@@ -288,21 +289,10 @@
 #else
   localtime_r(&t, &now);
 #endif
+  auto output_string =
+      StderrOutputGenerator(now, getpid(), GetThreadId(), severity, tag, file, line, message);
 
-  char timestamp[32];
-  strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
-
-  static const char log_characters[] = "VDIWEFF";
-  static_assert(arraysize(log_characters) - 1 == FATAL + 1,
-                "Mismatch in size of log_characters and values in LogSeverity");
-  char severity_char = log_characters[severity];
-  if (file != nullptr) {
-    fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char,
-            timestamp, getpid(), GetThreadId(), file, line, message);
-  } else {
-    fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s\n", tag ? tag : "nullptr", severity_char,
-            timestamp, getpid(), GetThreadId(), message);
-  }
+  fputs(output_string.c_str(), stderr);
 }
 
 void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/,
@@ -324,26 +314,9 @@
   abort();
 }
 
-
-LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {
-}
-
-void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag,
-                            const char* file, unsigned int line,
-                            const char* message) {
-  int32_t priority = LogSeverityToPriority(severity);
-  if (id == DEFAULT) {
-    id = default_log_id_;
-  }
-
+static void LogdLogChunk(LogId id, LogSeverity severity, const char* tag, const char* message) {
   int32_t lg_id = LogIdTolog_id_t(id);
-
-  char log_message_with_file[4068];  // LOGGER_ENTRY_MAX_PAYLOAD, not available in the NDK.
-  if (priority == ANDROID_LOG_FATAL && file != nullptr) {
-    snprintf(log_message_with_file, sizeof(log_message_with_file), "%s:%u] %s", file, line,
-             message);
-    message = log_message_with_file;
-  }
+  int32_t priority = LogSeverityToPriority(severity);
 
   static auto& liblog_functions = GetLibLogFunctions();
   if (liblog_functions) {
@@ -355,6 +328,17 @@
   }
 }
 
+LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {}
+
+void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag, const char* file,
+                            unsigned int line, const char* message) {
+  if (id == DEFAULT) {
+    id = default_log_id_;
+  }
+
+  SplitByLogdChunks(id, severity, tag, file, line, message, LogdLogChunk);
+}
+
 void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) {
   SetLogger(std::forward<LogFunction>(logger));
   SetAborter(std::forward<AbortFunction>(aborter));
@@ -515,26 +499,8 @@
 #endif
   }
 
-  {
-    // Do the actual logging with the lock held.
-    std::lock_guard<std::mutex> lock(LoggingLock());
-    if (msg.find('\n') == std::string::npos) {
-      LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
-              msg.c_str());
-    } else {
-      msg += '\n';
-      size_t i = 0;
-      while (i < msg.size()) {
-        size_t nl = msg.find('\n', i);
-        msg[nl] = '\0';
-        LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
-                &msg[i]);
-        // Undo the zero-termination so we can give the complete message to the aborter.
-        msg[nl] = '\n';
-        i = nl + 1;
-      }
-    }
-  }
+  LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
+          msg.c_str());
 
   // Abort if necessary.
   if (data_->GetSeverity() == FATAL) {
diff --git a/base/logging_splitters.h b/base/logging_splitters.h
new file mode 100644
index 0000000..2ec2b20
--- /dev/null
+++ b/base/logging_splitters.h
@@ -0,0 +1,185 @@
+/*
+ * 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 <inttypes.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#define LOGGER_ENTRY_MAX_PAYLOAD 4068  // This constant is not in the NDK.
+
+namespace android {
+namespace base {
+
+// This splits the message up line by line, by calling log_function with a pointer to the start of
+// each line and the size up to the newline character.  It sends size = -1 for the final line.
+template <typename F, typename... Args>
+static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
+  const char* newline = strchr(msg, '\n');
+  while (newline != nullptr) {
+    log_function(msg, newline - msg, args...);
+    msg = newline + 1;
+    newline = strchr(msg, '\n');
+  }
+
+  log_function(msg, -1, args...);
+}
+
+// This splits the message up into chunks that logs can process delimited by new lines.  It calls
+// log_function with the exact null terminated message that should be sent to logd.
+// Note, despite the loops and snprintf's, if severity is not fatal and there are no new lines,
+// this function simply calls log_function with msg without any extra overhead.
+template <typename F>
+static void SplitByLogdChunks(LogId log_id, LogSeverity severity, const char* tag, const char* file,
+                              unsigned int line, const char* msg, const F& log_function) {
+  // The maximum size of a payload, after the log header that logd will accept is
+  // LOGGER_ENTRY_MAX_PAYLOAD, so subtract the other elements in the payload to find the size of
+  // the string that we can log in each pass.
+  // The protocol is documented in liblog/README.protocol.md.
+  // Specifically we subtract a byte for the priority, the length of the tag + its null terminator,
+  // and an additional byte for the null terminator on the payload.  We subtract an additional 32
+  // bytes for slack, similar to java/android/util/Log.java.
+  ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
+  if (max_size <= 0) {
+    abort();
+  }
+  // If we're logging a fatal message, we'll append the file and line numbers.
+  bool add_file = file != nullptr && (severity == FATAL || severity == FATAL_WITHOUT_ABORT);
+
+  std::string file_header;
+  if (add_file) {
+    file_header = StringPrintf("%s:%u] ", file, line);
+  }
+  int file_header_size = file_header.size();
+
+  __attribute__((uninitialized)) char logd_chunk[max_size + 1];
+  ptrdiff_t chunk_position = 0;
+
+  auto call_log_function = [&]() {
+    log_function(log_id, severity, tag, logd_chunk);
+    chunk_position = 0;
+  };
+
+  auto write_to_logd_chunk = [&](const char* message, int length) {
+    int size_written = 0;
+    const char* new_line = chunk_position > 0 ? "\n" : "";
+    if (add_file) {
+      size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
+                              "%s%s%.*s", new_line, file_header.c_str(), length, message);
+    } else {
+      size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
+                              "%s%.*s", new_line, length, message);
+    }
+
+    // This should never fail, if it does and we set size_written to 0, which will skip this line
+    // and move to the next one.
+    if (size_written < 0) {
+      size_written = 0;
+    }
+    chunk_position += size_written;
+  };
+
+  const char* newline = strchr(msg, '\n');
+  while (newline != nullptr) {
+    // If we have data in the buffer and this next line doesn't fit, write the buffer.
+    if (chunk_position != 0 && chunk_position + (newline - msg) + 1 + file_header_size > max_size) {
+      call_log_function();
+    }
+
+    // Otherwise, either the next line fits or we have any empty buffer and too large of a line to
+    // ever fit, in both cases, we add it to the buffer and continue.
+    write_to_logd_chunk(msg, newline - msg);
+
+    msg = newline + 1;
+    newline = strchr(msg, '\n');
+  }
+
+  // If we have left over data in the buffer and we can fit the rest of msg, add it to the buffer
+  // then write the buffer.
+  if (chunk_position != 0 &&
+      chunk_position + static_cast<int>(strlen(msg)) + 1 + file_header_size <= max_size) {
+    write_to_logd_chunk(msg, -1);
+    call_log_function();
+  } else {
+    // If the buffer is not empty and we can't fit the rest of msg into it, write its contents.
+    if (chunk_position != 0) {
+      call_log_function();
+    }
+    // Then write the rest of the msg.
+    if (add_file) {
+      snprintf(logd_chunk, sizeof(logd_chunk), "%s%s", file_header.c_str(), msg);
+      log_function(log_id, severity, tag, logd_chunk);
+    } else {
+      log_function(log_id, severity, tag, msg);
+    }
+  }
+}
+
+static std::pair<int, int> CountSizeAndNewLines(const char* message) {
+  int size = 0;
+  int new_lines = 0;
+  while (*message != '\0') {
+    size++;
+    if (*message == '\n') {
+      ++new_lines;
+    }
+    ++message;
+  }
+  return {size, new_lines};
+}
+
+// This adds the log header to each line of message and returns it as a string intended to be
+// written to stderr.
+static std::string StderrOutputGenerator(const struct tm& now, int pid, uint64_t tid,
+                                         LogSeverity severity, const char* tag, const char* file,
+                                         unsigned int line, const char* message) {
+  char timestamp[32];
+  strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
+
+  static const char log_characters[] = "VDIWEFF";
+  static_assert(arraysize(log_characters) - 1 == FATAL + 1,
+                "Mismatch in size of log_characters and values in LogSeverity");
+  char severity_char = log_characters[severity];
+  std::string line_prefix;
+  if (file != nullptr) {
+    line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " %s:%u] ", tag ? tag : "nullptr",
+                               severity_char, timestamp, pid, tid, file, line);
+  } else {
+    line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " ", tag ? tag : "nullptr", severity_char,
+                               timestamp, pid, tid);
+  }
+
+  auto [size, new_lines] = CountSizeAndNewLines(message);
+  std::string output_string;
+  output_string.reserve(size + new_lines * line_prefix.size() + 1);
+
+  auto concat_lines = [&](const char* message, int size) {
+    output_string.append(line_prefix);
+    if (size == -1) {
+      output_string.append(message);
+    } else {
+      output_string.append(message, size);
+    }
+    output_string.append("\n");
+  };
+  SplitByLines(message, concat_lines);
+  return output_string;
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/logging_splitters_test.cpp b/base/logging_splitters_test.cpp
new file mode 100644
index 0000000..679d19e
--- /dev/null
+++ b/base/logging_splitters_test.cpp
@@ -0,0 +1,325 @@
+/*
+ * 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 "logging_splitters.h"
+
+#include <string>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+void TestNewlineSplitter(const std::string& input,
+                         const std::vector<std::string>& expected_output) {
+  std::vector<std::string> output;
+  auto logger_function = [&](const char* msg, int length) {
+    if (length == -1) {
+      output.push_back(msg);
+    } else {
+      output.push_back(std::string(msg, length));
+    }
+  };
+  SplitByLines(input.c_str(), logger_function);
+
+  EXPECT_EQ(expected_output, output);
+}
+
+TEST(logging_splitters, NewlineSplitter_EmptyString) {
+  TestNewlineSplitter("", std::vector<std::string>{""});
+}
+
+TEST(logging_splitters, NewlineSplitter_BasicString) {
+  TestNewlineSplitter("normal string", std::vector<std::string>{"normal string"});
+}
+
+TEST(logging_splitters, NewlineSplitter_ormalBasicStringTrailingNewline) {
+  TestNewlineSplitter("normal string\n", std::vector<std::string>{"normal string", ""});
+}
+
+TEST(logging_splitters, NewlineSplitter_MultilineTrailing) {
+  TestNewlineSplitter("normal string\nsecond string\nthirdstring",
+                      std::vector<std::string>{"normal string", "second string", "thirdstring"});
+}
+
+TEST(logging_splitters, NewlineSplitter_MultilineTrailingNewline) {
+  TestNewlineSplitter(
+      "normal string\nsecond string\nthirdstring\n",
+      std::vector<std::string>{"normal string", "second string", "thirdstring", ""});
+}
+
+TEST(logging_splitters, NewlineSplitter_MultilineEmbeddedNewlines) {
+  TestNewlineSplitter(
+      "normal string\n\n\nsecond string\n\nthirdstring\n",
+      std::vector<std::string>{"normal string", "", "", "second string", "", "thirdstring", ""});
+}
+
+void TestLogdChunkSplitter(const std::string& tag, const std::string& file,
+                           const std::string& input,
+                           const std::vector<std::string>& expected_output) {
+  std::vector<std::string> output;
+  auto logger_function = [&](LogId, LogSeverity, const char*, const char* msg) {
+    output.push_back(msg);
+  };
+
+  SplitByLogdChunks(MAIN, FATAL, tag.c_str(), file.empty() ? nullptr : file.c_str(), 1000,
+                    input.c_str(), logger_function);
+
+  auto return_lengths = [&] {
+    std::string sizes;
+    sizes += "expected_output sizes:";
+    for (const auto& string : expected_output) {
+      sizes += " " + std::to_string(string.size());
+    }
+    sizes += "\noutput sizes:";
+    for (const auto& string : output) {
+      sizes += " " + std::to_string(string.size());
+    }
+    return sizes;
+  };
+
+  EXPECT_EQ(expected_output, output) << return_lengths();
+}
+
+TEST(logging_splitters, LogdChunkSplitter_EmptyString) {
+  TestLogdChunkSplitter("tag", "", "", std::vector<std::string>{""});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_BasicString) {
+  TestLogdChunkSplitter("tag", "", "normal string", std::vector<std::string>{"normal string"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_NormalBasicStringTrailingNewline) {
+  TestLogdChunkSplitter("tag", "", "normal string\n", std::vector<std::string>{"normal string\n"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultilineTrailing) {
+  TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring",
+                        std::vector<std::string>{"normal string\nsecond string\nthirdstring"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultilineTrailingNewline) {
+  TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring\n",
+                        std::vector<std::string>{"normal string\nsecond string\nthirdstring\n"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultilineEmbeddedNewlines) {
+  TestLogdChunkSplitter(
+      "tag", "", "normal string\n\n\nsecond string\n\nthirdstring\n",
+      std::vector<std::string>{"normal string\n\n\nsecond string\n\nthirdstring\n"});
+}
+
+// This test should return the same string, the logd logger itself will truncate down to size.
+// This has historically been the behavior both in libbase and liblog.
+TEST(logging_splitters, LogdChunkSplitter_HugeLineNoNewline) {
+  auto long_string = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
+  ASSERT_EQ(LOGGER_ENTRY_MAX_PAYLOAD, static_cast<int>(long_string.size()));
+
+  TestLogdChunkSplitter("tag", "", long_string, std::vector{long_string});
+}
+
+std::string ReduceToMaxSize(const std::string& tag, const std::string& string) {
+  return string.substr(0, LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35);
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultipleHugeLineNoNewline) {
+  auto long_string_x = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
+  auto long_string_y = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'y');
+  auto long_string_z = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'z');
+
+  auto long_strings = long_string_x + '\n' + long_string_y + '\n' + long_string_z;
+
+  std::string tag = "tag";
+  std::vector expected = {ReduceToMaxSize(tag, long_string_x), ReduceToMaxSize(tag, long_string_y),
+                          long_string_z};
+
+  TestLogdChunkSplitter(tag, "", long_strings, expected);
+}
+
+// With a ~4k buffer, we should print 2 long strings per logger call.
+TEST(logging_splitters, LogdChunkSplitter_Multiple2kLines) {
+  std::vector expected = {
+      std::string(2000, 'a') + '\n' + std::string(2000, 'b'),
+      std::string(2000, 'c') + '\n' + std::string(2000, 'd'),
+      std::string(2000, 'e') + '\n' + std::string(2000, 'f'),
+  };
+
+  auto long_strings = Join(expected, '\n');
+
+  TestLogdChunkSplitter("tag", "", long_strings, expected);
+}
+
+TEST(logging_splitters, LogdChunkSplitter_ExactSizedLines) {
+  const char* tag = "tag";
+  ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
+  auto long_string_a = std::string(max_size, 'a');
+  auto long_string_b = std::string(max_size, 'b');
+  auto long_string_c = std::string(max_size, 'c');
+
+  auto long_strings = long_string_a + '\n' + long_string_b + '\n' + long_string_c;
+
+  TestLogdChunkSplitter(tag, "", long_strings,
+                        std::vector{long_string_a, long_string_b, long_string_c});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_UnderEqualOver) {
+  std::string tag = "tag";
+  ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
+
+  auto first_string_size = 1000;
+  auto first_string = std::string(first_string_size, 'a');
+  auto second_string_size = max_size - first_string_size - 1;
+  auto second_string = std::string(second_string_size, 'b');
+
+  auto exact_string = std::string(max_size, 'c');
+
+  auto large_string = std::string(max_size + 50, 'd');
+
+  auto final_string = std::string("final string!\n\nfinal \n \n final \n");
+
+  std::vector expected = {first_string + '\n' + second_string, exact_string,
+                          ReduceToMaxSize(tag, large_string), final_string};
+
+  std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
+                               final_string};
+  auto long_strings = Join(input_strings, '\n');
+
+  TestLogdChunkSplitter(tag, "", long_strings, expected);
+}
+
+TEST(logging_splitters, LogdChunkSplitter_WithFile) {
+  std::string tag = "tag";
+  std::string file = "/path/to/myfile.cpp";
+  int line = 1000;
+  auto file_header = StringPrintf("%s:%d] ", file.c_str(), line);
+  ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
+
+  auto first_string_size = 1000;
+  auto first_string = std::string(first_string_size, 'a');
+  auto second_string_size = max_size - first_string_size - 1 - 2 * file_header.size();
+  auto second_string = std::string(second_string_size, 'b');
+
+  auto exact_string = std::string(max_size - file_header.size(), 'c');
+
+  auto large_string = std::string(max_size + 50, 'd');
+
+  auto final_string = std::string("final string!");
+
+  std::vector expected = {
+      file_header + first_string + '\n' + file_header + second_string, file_header + exact_string,
+      file_header + ReduceToMaxSize(file_header + tag, large_string), file_header + final_string};
+
+  std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
+                               final_string};
+  auto long_strings = Join(input_strings, '\n');
+
+  TestLogdChunkSplitter(tag, file, long_strings, expected);
+}
+
+// We set max_size based off of tag, so if it's too large, the buffer will be sized wrong.
+// We could recover from this, but it's certainly an error for someone to attempt to use a tag this
+// large, so we abort instead.
+TEST(logging_splitters, LogdChunkSplitter_TooLongTag) {
+  auto long_tag = std::string(5000, 'x');
+  auto logger_function = [](LogId, LogSeverity, const char*, const char*) {};
+  ASSERT_DEATH(
+      SplitByLogdChunks(MAIN, ERROR, long_tag.c_str(), nullptr, 0, "message", logger_function), "");
+}
+
+// We do handle excessively large file names correctly however.
+TEST(logging_splitters, LogdChunkSplitter_TooLongFile) {
+  auto long_file = std::string(5000, 'x');
+  std::string tag = "tag";
+
+  std::vector expected = {ReduceToMaxSize(tag, long_file), ReduceToMaxSize(tag, long_file)};
+
+  TestLogdChunkSplitter(tag, long_file, "can't see me\nor me", expected);
+}
+
+void TestStderrOutputGenerator(const char* tag, const char* file, int line, const char* message,
+                               const std::string& expected) {
+  // All log messages will show "01-01 00:00:00"
+  struct tm now = {
+      .tm_sec = 0,
+      .tm_min = 0,
+      .tm_hour = 0,
+      .tm_mday = 1,
+      .tm_mon = 0,
+      .tm_year = 1970,
+  };
+
+  int pid = 1234;       // All log messages will have 1234 for their PID.
+  uint64_t tid = 4321;  // All log messages will have 4321 for their TID.
+
+  auto result = StderrOutputGenerator(now, pid, tid, ERROR, tag, file, line, message);
+  EXPECT_EQ(expected, result);
+}
+
+TEST(logging_splitters, StderrOutputGenerator_Basic) {
+  TestStderrOutputGenerator(nullptr, nullptr, 0, "simple message",
+                            "nullptr E 01-01 00:00:00  1234  4321 simple message\n");
+  TestStderrOutputGenerator("tag", nullptr, 0, "simple message",
+                            "tag E 01-01 00:00:00  1234  4321 simple message\n");
+  TestStderrOutputGenerator(
+      "tag", "/path/to/some/file", 0, "simple message",
+      "tag E 01-01 00:00:00  1234  4321 /path/to/some/file:0] simple message\n");
+}
+
+TEST(logging_splitters, StderrOutputGenerator_NewlineTagAndFile) {
+  TestStderrOutputGenerator("tag\n\n", nullptr, 0, "simple message",
+                            "tag\n\n E 01-01 00:00:00  1234  4321 simple message\n");
+  TestStderrOutputGenerator(
+      "tag", "/path/to/some/file\n\n", 0, "simple message",
+      "tag E 01-01 00:00:00  1234  4321 /path/to/some/file\n\n:0] simple message\n");
+}
+
+TEST(logging_splitters, StderrOutputGenerator_TrailingNewLine) {
+  TestStderrOutputGenerator(
+      "tag", nullptr, 0, "simple message\n",
+      "tag E 01-01 00:00:00  1234  4321 simple message\ntag E 01-01 00:00:00  1234  4321 \n");
+}
+
+TEST(logging_splitters, StderrOutputGenerator_MultiLine) {
+  const char* expected_result =
+      "tag E 01-01 00:00:00  1234  4321 simple message\n"
+      "tag E 01-01 00:00:00  1234  4321 \n"
+      "tag E 01-01 00:00:00  1234  4321 \n"
+      "tag E 01-01 00:00:00  1234  4321 another message \n"
+      "tag E 01-01 00:00:00  1234  4321 \n"
+      "tag E 01-01 00:00:00  1234  4321  final message \n"
+      "tag E 01-01 00:00:00  1234  4321 \n"
+      "tag E 01-01 00:00:00  1234  4321 \n"
+      "tag E 01-01 00:00:00  1234  4321 \n";
+
+  TestStderrOutputGenerator("tag", nullptr, 0,
+                            "simple message\n\n\nanother message \n\n final message \n\n\n",
+                            expected_result);
+}
+
+TEST(logging_splitters, StderrOutputGenerator_MultiLineLong) {
+  auto long_string_a = std::string(4000, 'a');
+  auto long_string_b = std::string(4000, 'b');
+
+  auto message = long_string_a + '\n' + long_string_b;
+  auto expected_result = "tag E 01-01 00:00:00  1234  4321 " + long_string_a + '\n' +
+                         "tag E 01-01 00:00:00  1234  4321 " + long_string_b + '\n';
+  TestStderrOutputGenerator("tag", nullptr, 0, message.c_str(), expected_result);
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 3a453e6..593e2c1 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -24,8 +24,10 @@
 
 #include <regex>
 #include <string>
+#include <thread>
 
 #include "android-base/file.h"
+#include "android-base/scopeguard.h"
 #include "android-base/stringprintf.h"
 #include "android-base/test_utils.h"
 
@@ -596,7 +598,7 @@
   CapturedStderr cap;
   LOG(FATAL) << "foo\nbar";
 
-  EXPECT_EQ(CountLineAborter::newline_count, 1U + 1U);  // +1 for final '\n'.
+  EXPECT_EQ(CountLineAborter::newline_count, 1U);
 }
 
 __attribute__((constructor)) void TestLoggingInConstructor() {
@@ -617,3 +619,55 @@
   // Whereas ERROR logging includes the program name.
   ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", cap_err.str());
 }
+
+TEST(logging, ForkSafe) {
+#if !defined(_WIN32)
+  using namespace android::base;
+  SetLogger(
+      [&](LogId, LogSeverity, const char*, const char*, unsigned int, const char*) { sleep(3); });
+
+  auto guard = make_scope_guard([&] {
+#ifdef __ANDROID__
+    SetLogger(LogdLogger());
+#else
+    SetLogger(StderrLogger);
+#endif
+  });
+
+  auto thread = std::thread([] {
+    LOG(ERROR) << "This should sleep for 3 seconds, long enough to fork another process, if there "
+                  "is no intervention";
+  });
+  thread.detach();
+
+  auto pid = fork();
+  ASSERT_NE(-1, pid);
+
+  if (pid == 0) {
+    // Reset the logger, so the next message doesn't sleep().
+    SetLogger([](LogId, LogSeverity, const char*, const char*, unsigned int, const char*) {});
+    LOG(ERROR) << "This should succeed in the child, only if libbase is forksafe.";
+    _exit(EXIT_SUCCESS);
+  }
+
+  // Wait for up to 3 seconds for the child to exit.
+  int tries = 3;
+  bool found_child = false;
+  while (tries-- > 0) {
+    auto result = waitpid(pid, nullptr, WNOHANG);
+    EXPECT_NE(-1, result);
+    if (result == pid) {
+      found_child = true;
+      break;
+    }
+    sleep(1);
+  }
+
+  EXPECT_TRUE(found_child);
+
+  // Kill the child if it did not exit.
+  if (!found_child) {
+    kill(pid, SIGKILL);
+  }
+#endif
+}
diff --git a/base/stringprintf.cpp b/base/stringprintf.cpp
index 78e1e8d..e83ab13 100644
--- a/base/stringprintf.cpp
+++ b/base/stringprintf.cpp
@@ -25,7 +25,7 @@
 
 void StringAppendV(std::string* dst, const char* format, va_list ap) {
   // First try with a small fixed size buffer
-  char space[1024];
+  char space[1024] __attribute__((__uninitialized__));
 
   // It's possible for methods that use a va_list to invalidate
   // the data in it upon use.  The fix is to make a copy
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index d67b522..31c2d5d 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -169,6 +169,7 @@
         "libdebuggerd/backtrace.cpp",
         "libdebuggerd/gwp_asan.cpp",
         "libdebuggerd/open_files_list.cpp",
+        "libdebuggerd/scudo.cpp",
         "libdebuggerd/tombstone.cpp",
         "libdebuggerd/utility.cpp",
     ],
@@ -176,8 +177,13 @@
     local_include_dirs: ["libdebuggerd/include"],
     export_include_dirs: ["libdebuggerd/include"],
 
-    // Needed for private/bionic_fdsan.h
-    include_dirs: ["bionic/libc"],
+    include_dirs: [
+        // Needed for private/bionic_fdsan.h
+        "bionic/libc",
+
+        // Needed for scudo/interface.h
+        "external/scudo/standalone/include",
+    ],
     header_libs: [
         "bionic_libc_platform_headers",
         "gwp_asan_headers",
@@ -192,7 +198,10 @@
         "liblog",
     ],
 
-    whole_static_libs: ["gwp_asan_crash_handler"],
+    whole_static_libs: [
+        "gwp_asan_crash_handler",
+        "libscudo",
+    ],
 
     target: {
         recovery: {
@@ -206,6 +215,9 @@
         debuggable: {
             cflags: ["-DROOT_POSSIBLE"],
         },
+        experimental_mte: {
+            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+        },
     },
 }
 
@@ -256,6 +268,10 @@
         "gwp_asan_headers",
     ],
 
+    include_dirs: [
+        "external/scudo/standalone/include",
+    ],
+
     local_include_dirs: [
         "libdebuggerd",
     ],
@@ -271,6 +287,12 @@
     },
 
     test_suites: ["device-tests"],
+
+    product_variables: {
+        experimental_mte: {
+            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+        },
+    },
 }
 
 cc_benchmark {
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 0cd2350..d7cb972 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -289,6 +289,8 @@
       process_info->fdsan_table_address = crash_info->data.d.fdsan_table_address;
       process_info->gwp_asan_state = crash_info->data.d.gwp_asan_state;
       process_info->gwp_asan_metadata = crash_info->data.d.gwp_asan_metadata;
+      process_info->scudo_stack_depot = crash_info->data.d.scudo_stack_depot;
+      process_info->scudo_region_info = crash_info->data.d.scudo_region_info;
       FALLTHROUGH_INTENDED;
     case 1:
     case 2:
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 054f836..9d7658e 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -31,6 +31,9 @@
 
 #include <android/fdsan.h>
 #include <android/set_abort_message.h>
+#include <bionic/malloc.h>
+#include <bionic/mte.h>
+#include <bionic/mte_kernel.h>
 #include <bionic/reserved_signals.h>
 
 #include <android-base/cmsg.h>
@@ -331,6 +334,184 @@
       R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr (0x100000000000dead|0xdead))");
 }
 
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+static void SetTagCheckingLevelSync() {
+  int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+  if (tagged_addr_ctrl < 0) {
+    abort();
+  }
+
+  tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_SYNC;
+  if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) != 0) {
+    abort();
+  }
+
+  HeapTaggingLevel heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC;
+  if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level))) {
+    abort();
+  }
+}
+#endif
+
+TEST_F(CrasherTest, mte_uaf) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+  if (!mte_supported()) {
+    GTEST_SKIP() << "Requires MTE";
+  }
+
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() {
+    SetTagCheckingLevelSync();
+    volatile int* p = (volatile int*)malloc(16);
+    free((void *)p);
+    p[0] = 42;
+  });
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\))");
+  ASSERT_MATCH(result, R"(Cause: \[MTE\]: Use After Free, 0 bytes into a 16-byte allocation.*
+
+allocated by thread .*
+      #00 pc)");
+  ASSERT_MATCH(result, R"(deallocated by thread .*
+      #00 pc)");
+#else
+  GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
+TEST_F(CrasherTest, mte_overflow) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+  if (!mte_supported()) {
+    GTEST_SKIP() << "Requires MTE";
+  }
+
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() {
+    SetTagCheckingLevelSync();
+    volatile int* p = (volatile int*)malloc(16);
+    p[4] = 42;
+  });
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
+  ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation.*
+
+allocated by thread .*
+      #00 pc)");
+#else
+  GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
+TEST_F(CrasherTest, mte_underflow) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+  if (!mte_supported()) {
+    GTEST_SKIP() << "Requires MTE";
+  }
+
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() {
+    SetTagCheckingLevelSync();
+    volatile int* p = (volatile int*)malloc(16);
+    p[-1] = 42;
+  });
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\))");
+  ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Underflow, 4 bytes left of a 16-byte allocation.*
+
+allocated by thread .*
+      #00 pc)");
+#else
+  GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
+TEST_F(CrasherTest, mte_multiple_causes) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+  if (!mte_supported()) {
+    GTEST_SKIP() << "Requires MTE";
+  }
+
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() {
+    SetTagCheckingLevelSync();
+
+    // Make two allocations with the same tag and close to one another. Check for both properties
+    // with a bounds check -- this relies on the fact that only if the allocations have the same tag
+    // would they be measured as closer than 128 bytes to each other. Otherwise they would be about
+    // (some non-zero value << 56) apart.
+    //
+    // The out-of-bounds access will be considered either an overflow of one or an underflow of the
+    // other.
+    std::set<uintptr_t> allocs;
+    for (int i = 0; i != 4096; ++i) {
+      uintptr_t alloc = reinterpret_cast<uintptr_t>(malloc(16));
+      auto it = allocs.insert(alloc).first;
+      if (it != allocs.begin() && *std::prev(it) + 128 > alloc) {
+        *reinterpret_cast<int*>(*std::prev(it) + 16) = 42;
+      }
+      if (std::next(it) != allocs.end() && alloc + 128 > *std::next(it)) {
+        *reinterpret_cast<int*>(alloc + 16) = 42;
+      }
+    }
+  });
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
+  ASSERT_MATCH(
+      result,
+      R"(Note: multiple potential causes for this crash were detected, listing them in decreasing order of probability.)");
+
+  // Adjacent untracked allocations may cause us to see the wrong underflow here (or only
+  // overflows), so we can't match explicitly for an underflow message.
+  ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation)");
+#else
+  GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
 TEST_F(CrasherTest, LD_PRELOAD) {
   int intercept_result;
   unique_fd output_fd;
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index 6650294..254ed4f 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -40,6 +40,8 @@
   void* fdsan_table;
   const gwp_asan::AllocatorState* gwp_asan_state;
   const gwp_asan::AllocationMetadata* gwp_asan_metadata;
+  const char* scudo_stack_depot;
+  const char* scudo_region_info;
 };
 
 // These callbacks are called in a signal handler, and thus must be async signal safe.
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
new file mode 100644
index 0000000..4d00ece
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.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 "types.h"
+#include "utility.h"
+
+#include <memory.h>
+
+#include "scudo/interface.h"
+
+class ScudoCrashData {
+ public:
+  ScudoCrashData() = delete;
+  ~ScudoCrashData() = default;
+  ScudoCrashData(unwindstack::Memory* process_memory, const ProcessInfo& process_info);
+
+  bool CrashIsMine() const;
+
+  void DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const;
+
+ private:
+  scudo_error_info error_info_ = {};
+  uintptr_t untagged_fault_addr_;
+
+  void DumpReport(const scudo_error_report* report, log_t* log,
+                  unwindstack::Unwinder* unwinder) const;
+};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index 35c3fd6..04c4b5c 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -41,6 +41,8 @@
   uintptr_t fdsan_table_address = 0;
   uintptr_t gwp_asan_state = 0;
   uintptr_t gwp_asan_metadata = 0;
+  uintptr_t scudo_stack_depot = 0;
+  uintptr_t scudo_region_info = 0;
 
   bool has_fault_address = false;
   uintptr_t fault_address = 0;
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
new file mode 100644
index 0000000..f8bfe07
--- /dev/null
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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 "libdebuggerd/scudo.h"
+#include "libdebuggerd/gwp_asan.h"
+
+#include "unwindstack/Memory.h"
+#include "unwindstack/Unwinder.h"
+
+#include <bionic/macros.h>
+
+std::unique_ptr<char[]> AllocAndReadFully(unwindstack::Memory* process_memory, uint64_t addr,
+                                          size_t size) {
+  auto buf = std::make_unique<char[]>(size);
+  if (!process_memory->ReadFully(addr, buf.get(), size)) {
+    return std::unique_ptr<char[]>();
+  }
+  return buf;
+}
+
+static const uintptr_t kTagGranuleSize = 16;
+
+ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory,
+                               const ProcessInfo& process_info) {
+  if (!process_info.has_fault_address) {
+    return;
+  }
+
+  auto stack_depot = AllocAndReadFully(process_memory, process_info.scudo_stack_depot,
+                                       __scudo_get_stack_depot_size());
+  auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,
+                                       __scudo_get_region_info_size());
+
+  untagged_fault_addr_ = untag_address(process_info.fault_address);
+  uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1);
+
+  uintptr_t memory_begin = fault_page - PAGE_SIZE * 16;
+  if (memory_begin > fault_page) {
+    return;
+  }
+
+  uintptr_t memory_end = fault_page + PAGE_SIZE * 16;
+  if (memory_end < fault_page) {
+    return;
+  }
+
+  auto memory = std::make_unique<char[]>(memory_end - memory_begin);
+  for (auto i = memory_begin; i != memory_end; i += PAGE_SIZE) {
+    process_memory->ReadFully(i, memory.get() + i - memory_begin, PAGE_SIZE);
+  }
+
+  auto memory_tags = std::make_unique<char[]>((memory_end - memory_begin) / kTagGranuleSize);
+  for (auto i = memory_begin; i != memory_end; i += kTagGranuleSize) {
+    memory_tags[(i - memory_begin) / kTagGranuleSize] = process_memory->ReadTag(i);
+  }
+
+  __scudo_get_error_info(&error_info_, process_info.fault_address, stack_depot.get(),
+                         region_info.get(), memory.get(), memory_tags.get(), memory_begin,
+                         memory_end - memory_begin);
+}
+
+bool ScudoCrashData::CrashIsMine() const {
+  return error_info_.reports[0].error_type != UNKNOWN;
+}
+
+void ScudoCrashData::DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const {
+  if (error_info_.reports[1].error_type != UNKNOWN) {
+    _LOG(log, logtype::HEADER,
+         "\nNote: multiple potential causes for this crash were detected, listing them in "
+         "decreasing order of probability.\n");
+  }
+
+  size_t report_num = 0;
+  while (report_num < sizeof(error_info_.reports) / sizeof(error_info_.reports[0]) &&
+         error_info_.reports[report_num].error_type != UNKNOWN) {
+    DumpReport(&error_info_.reports[report_num++], log, unwinder);
+  }
+}
+
+void ScudoCrashData::DumpReport(const scudo_error_report* report, log_t* log,
+                                unwindstack::Unwinder* unwinder) const {
+  const char *error_type_str;
+  switch (report->error_type) {
+    case USE_AFTER_FREE:
+      error_type_str = "Use After Free";
+      break;
+    case BUFFER_OVERFLOW:
+      error_type_str = "Buffer Overflow";
+      break;
+    case BUFFER_UNDERFLOW:
+      error_type_str = "Buffer Underflow";
+      break;
+    default:
+      error_type_str = "Unknown";
+      break;
+  }
+
+  uintptr_t diff;
+  const char* location_str;
+
+  if (untagged_fault_addr_ < report->allocation_address) {
+    // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
+    location_str = "left of";
+    diff = report->allocation_address - untagged_fault_addr_;
+  } else if (untagged_fault_addr_ - report->allocation_address < report->allocation_size) {
+    // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
+    location_str = "into";
+    diff = untagged_fault_addr_ - report->allocation_address;
+  } else {
+    // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef.
+    location_str = "right of";
+    diff = untagged_fault_addr_ - report->allocation_address - report->allocation_size;
+  }
+
+  // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
+  const char* byte_suffix = "s";
+  if (diff == 1) {
+    byte_suffix = "";
+  }
+  _LOG(log, logtype::HEADER,
+       "\nCause: [MTE]: %s, %" PRIuPTR " byte%s %s a %zu-byte allocation at 0x%" PRIxPTR "\n",
+       error_type_str, diff, byte_suffix, location_str, report->allocation_size,
+       report->allocation_address);
+
+  if (report->allocation_trace[0]) {
+    _LOG(log, logtype::BACKTRACE, "\nallocated by thread %u:\n", report->allocation_tid);
+    unwinder->SetDisplayBuildID(true);
+    for (size_t i = 0; i < 64 && report->allocation_trace[i]; ++i) {
+      unwindstack::FrameData frame_data =
+          unwinder->BuildFrameFromPcOnly(report->allocation_trace[i]);
+      frame_data.num = i;
+      _LOG(log, logtype::BACKTRACE, "    %s\n", unwinder->FormatFrame(frame_data).c_str());
+    }
+  }
+
+  if (report->deallocation_trace[0]) {
+    _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %u:\n", report->deallocation_tid);
+    unwinder->SetDisplayBuildID(true);
+    for (size_t i = 0; i < 64 && report->deallocation_trace[i]; ++i) {
+      unwindstack::FrameData frame_data =
+          unwinder->BuildFrameFromPcOnly(report->deallocation_trace[i]);
+      frame_data.num = i;
+      _LOG(log, logtype::BACKTRACE, "    %s\n", unwinder->FormatFrame(frame_data).c_str());
+    }
+  }
+}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index e0168d5..ab65dd1 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -43,6 +43,7 @@
 #include <android-base/unique_fd.h>
 #include <android/log.h>
 #include <log/log.h>
+#include <log/log_read.h>
 #include <log/logprint.h>
 #include <private/android_filesystem_config.h>
 #include <unwindstack/DexFiles.h>
@@ -55,6 +56,7 @@
 #include "libdebuggerd/backtrace.h"
 #include "libdebuggerd/gwp_asan.h"
 #include "libdebuggerd/open_files_list.h"
+#include "libdebuggerd/scudo.h"
 #include "libdebuggerd/utility.h"
 
 #include "gwp_asan/common.h"
@@ -388,14 +390,17 @@
   }
 
   std::unique_ptr<GwpAsanCrashData> gwp_asan_crash_data;
+  std::unique_ptr<ScudoCrashData> scudo_crash_data;
   if (primary_thread) {
     gwp_asan_crash_data = std::make_unique<GwpAsanCrashData>(unwinder->GetProcessMemory().get(),
                                                              process_info, thread_info);
+    scudo_crash_data =
+        std::make_unique<ScudoCrashData>(unwinder->GetProcessMemory().get(), process_info);
   }
 
   if (primary_thread && gwp_asan_crash_data->CrashIsMine()) {
     gwp_asan_crash_data->DumpCause(log);
-  } else if (thread_info.siginfo) {
+  } else if (thread_info.siginfo && !(primary_thread && scudo_crash_data->CrashIsMine())) {
     dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(),
                         thread_info.registers.get());
   }
@@ -426,6 +431,8 @@
       gwp_asan_crash_data->DumpAllocationTrace(log, unwinder);
     }
 
+    scudo_crash_data->DumpCause(log, unwinder);
+
     unwindstack::Maps* maps = unwinder->GetMaps();
     dump_memory_and_code(log, maps, unwinder->GetProcessMemory().get(),
                          thread_info.registers.get());
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 3bf28b6..c8a3431 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -35,6 +35,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <bionic/mte_kernel.h>
 #include <bionic/reserved_signals.h>
 #include <debuggerd/handler.h>
 #include <log/log.h>
@@ -374,6 +375,12 @@
           return "SEGV_ADIDERR";
         case SEGV_ADIPERR:
           return "SEGV_ADIPERR";
+#if defined(ANDROID_EXPERIMENTAL_MTE)
+        case SEGV_MTEAERR:
+          return "SEGV_MTEAERR";
+        case SEGV_MTESERR:
+          return "SEGV_MTESERR";
+#endif
       }
       static_assert(NSIGSEGV == SEGV_ADIPERR, "missing SEGV_* si_code");
       break;
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index e85660c..53a76ea 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -95,6 +95,8 @@
   uintptr_t fdsan_table_address;
   uintptr_t gwp_asan_state;
   uintptr_t gwp_asan_metadata;
+  uintptr_t scudo_stack_depot;
+  uintptr_t scudo_region_info;
 };
 
 struct __attribute__((__packed__)) CrashInfo {
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index b8eee4a..2553353 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -625,7 +625,7 @@
         if (!sm) {
             return device->WriteFail("Unable to create SnapshotManager");
         }
-        if (!sm->HandleImminentDataWipe()) {
+        if (!sm->FinishMergeInRecovery()) {
             return device->WriteFail("Unable to finish snapshot merge");
         }
     } else {
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index f579078..ca782b9 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -42,7 +42,7 @@
     $ adb push <source> <destination>
     $ adb reboot
 
-Note that you can replace these two lines:
+Note that you can replace these two lines in the above sequence:
 
     $ adb disable-verity
     $ adb reboot
@@ -51,7 +51,7 @@
 
     $ adb remount -R
 
-**Note:** _adb reboot -R_ won’t reboot if the device is already in the adb remount state.
+**Note:** _adb remount -R_ won’t reboot if the device is already in the adb remount state.
 
 None of this changes if OverlayFS needs to be engaged.
 The decisions whether to use traditional direct file-system remount,
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 24cbad7..052efa7 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -105,20 +105,23 @@
 
 }  // namespace
 
+enum RemountStatus {
+    REMOUNT_SUCCESS = 0,
+    NOT_USERDEBUG,
+    BADARG,
+    NOT_ROOT,
+    NO_FSTAB,
+    UNKNOWN_PARTITION,
+    INVALID_PARTITION,
+    VERITY_PARTITION,
+    BAD_OVERLAY,
+    NO_MOUNTS,
+    REMOUNT_FAILED,
+    MUST_REBOOT
+};
+
 static int do_remount(int argc, char* argv[]) {
-    enum {
-        SUCCESS = 0,
-        NOT_USERDEBUG,
-        BADARG,
-        NOT_ROOT,
-        NO_FSTAB,
-        UNKNOWN_PARTITION,
-        INVALID_PARTITION,
-        VERITY_PARTITION,
-        BAD_OVERLAY,
-        NO_MOUNTS,
-        REMOUNT_FAILED,
-    } retval = SUCCESS;
+    RemountStatus retval = REMOUNT_SUCCESS;
 
     // If somehow this executable is delivered on a "user" build, it can
     // not function, so providing a clear message to the caller rather than
@@ -304,8 +307,7 @@
     if (partitions.empty() || just_disabled_verity) {
         if (reboot_later) reboot(setup_overlayfs);
         if (user_please_reboot_later) {
-            LOG(INFO) << "Now reboot your device for settings to take effect";
-            return 0;
+            return MUST_REBOOT;
         }
         LOG(WARNING) << "No partitions to remount";
         return retval;
@@ -394,6 +396,12 @@
 int main(int argc, char* argv[]) {
     android::base::InitLogging(argv, MyLogger);
     int result = do_remount(argc, argv);
-    printf("remount %s\n", result ? "failed" : "succeeded");
+    if (result == MUST_REBOOT) {
+        LOG(INFO) << "Now reboot your device for settings to take effect";
+    } else if (result == REMOUNT_SUCCESS) {
+        printf("remount succeeded\n");
+    } else {
+        printf("remount failed\n");
+    }
     return result;
 }
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index 9d18a44..cae43e6 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -69,6 +69,7 @@
         "libdm",
         "libfs_mgr",
         "liblog",
+        "libgsi",
     ],
 
     data: [
@@ -90,6 +91,7 @@
 cc_test {
     name: "fiemap_image_test",
     static_libs: [
+        "libcrypto_utils",
         "libdm",
         "libext4_utils",
         "libfs_mgr",
@@ -98,7 +100,6 @@
     shared_libs: [
         "libbase",
         "libcrypto",
-        "libcrypto_utils",
         "libcutils",
         "liblog",
     ],
@@ -117,6 +118,7 @@
         "-DSKIP_TEST_IN_PRESUBMIT",
     ],
     static_libs: [
+        "libcrypto_utils",
         "libdm",
         "libext4_utils",
         "libfs_mgr",
@@ -125,7 +127,6 @@
     shared_libs: [
         "libbase",
         "libcrypto",
-        "libcrypto_utils",
         "libcutils",
         "liblog",
     ],
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index 22a3722..3c8ab42 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -35,6 +35,7 @@
 #include <libdm/loop_control.h>
 #include <libfiemap/fiemap_writer.h>
 #include <libfiemap/split_fiemap_writer.h>
+#include <libgsi/libgsi.h>
 
 #include "utility.h"
 
@@ -148,7 +149,10 @@
     FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
     EXPECT_EQ(fptr->size(), gBlockSize);
     EXPECT_EQ(fptr->bdev_path().find("/dev/block/"), size_t(0));
-    EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
+
+    if (!android::gsi::IsGsiRunning()) {
+        EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
+    }
 }
 
 TEST_F(FiemapWriterTest, CheckFileCreated) {
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 5388b44..6663391 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -131,132 +131,6 @@
     ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
 }
 
-// This fixture is for tests against a simulated device environment. Rather
-// than use /data, we create an image and then layer a new filesystem within
-// it. Each test then decides how to mount and create layered images. This
-// allows us to test FBE vs FDE configurations.
-class ImageTest : public ::testing::Test {
-  public:
-    ImageTest() : dm_(DeviceMapper::Instance()) {}
-
-    void SetUp() override {
-        manager_ = ImageManager::Open(kMetadataPath, gDataPath);
-        ASSERT_NE(manager_, nullptr);
-
-        manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
-        submanager_ = ImageManager::Open(kMetadataPath + "/mnt"s, gDataPath + "/mnt"s);
-        ASSERT_NE(submanager_, nullptr);
-
-        submanager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
-        // Ensure that metadata is cleared in between runs.
-        submanager_->RemoveAllImages();
-        manager_->RemoveAllImages();
-
-        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
-        base_name_ = tinfo->name();
-        test_image_name_ = base_name_ + "-base";
-        wrapper_device_name_ = base_name_ + "-wrapper";
-
-        ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize * 16, false, nullptr));
-        ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &base_device_));
-    }
-
-    void TearDown() override {
-        submanager_->UnmapImageDevice(test_image_name_);
-        umount(gDataMountPath.c_str());
-        dm_.DeleteDeviceIfExists(wrapper_device_name_);
-        manager_->UnmapImageDevice(base_name_);
-        manager_->DeleteBackingImage(base_name_);
-    }
-
-  protected:
-    bool DoFormat(const std::string& device) {
-        // clang-format off
-        std::vector<std::string> mkfs_args = {
-            "/system/bin/mke2fs",
-            "-F",
-            "-b 4096",
-            "-t ext4",
-            "-m 0",
-            "-O has_journal",
-            device,
-            ">/dev/null",
-            "2>/dev/null",
-            "</dev/null",
-        };
-        // clang-format on
-        auto command = android::base::Join(mkfs_args, " ");
-        return system(command.c_str()) == 0;
-    }
-
-    std::unique_ptr<ImageManager> manager_;
-    std::unique_ptr<ImageManager> submanager_;
-
-    DeviceMapper& dm_;
-    std::string base_name_;
-    std::string base_device_;
-    std::string test_image_name_;
-    std::string wrapper_device_name_;
-};
-
-TEST_F(ImageTest, DirectMount) {
-    ASSERT_TRUE(DoFormat(base_device_));
-    ASSERT_EQ(mount(base_device_.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
-    ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
-
-    std::string path;
-    ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
-    ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/loop"));
-}
-
-TEST_F(ImageTest, IndirectMount) {
-#ifdef SKIP_TEST_IN_PRESUBMIT
-    GTEST_SKIP() << "WIP failure b/148874852";
-#endif
-    // Create a simple wrapper around the base device that we'll mount from
-    // instead. This will simulate the code paths for dm-crypt/default-key/bow
-    // and force us to use device-mapper rather than loop devices.
-    uint64_t device_size = 0;
-    {
-        unique_fd fd(open(base_device_.c_str(), O_RDWR | O_CLOEXEC));
-        ASSERT_GE(fd, 0);
-        device_size = get_block_device_size(fd);
-        ASSERT_EQ(device_size, kTestImageSize * 16);
-    }
-    uint64_t num_sectors = device_size / 512;
-
-    auto& dm = DeviceMapper::Instance();
-
-    DmTable table;
-    table.Emplace<DmTargetLinear>(0, num_sectors, base_device_, 0);
-    ASSERT_TRUE(dm.CreateDevice(wrapper_device_name_, table));
-
-    // Format and mount.
-    std::string wrapper_device;
-    ASSERT_TRUE(dm.GetDmDevicePathByName(wrapper_device_name_, &wrapper_device));
-    ASSERT_TRUE(WaitForFile(wrapper_device, 5s));
-    ASSERT_TRUE(DoFormat(wrapper_device));
-    ASSERT_EQ(mount(wrapper_device.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
-
-    ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
-
-    std::set<std::string> backing_devices;
-    auto init = [&](std::set<std::string> devices) -> bool {
-        backing_devices = std::move(devices);
-        return true;
-    };
-
-    std::string path;
-    ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
-    ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/dm-"));
-    ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
-    ASSERT_TRUE(submanager_->MapAllImages(init));
-    ASSERT_FALSE(backing_devices.empty());
-    ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
-}
-
 bool Mkdir(const std::string& path) {
     if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
         std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 2f516fa..c37d70e 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -19,6 +19,7 @@
 #include <string.h>
 
 #include <algorithm>
+#include <limits>
 
 #include <android-base/unique_fd.h>
 
@@ -369,7 +370,10 @@
     }
 
     // Align the metadata size up to the nearest sector.
-    metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
+    if (!AlignTo(metadata_max_size, LP_SECTOR_SIZE, &metadata_max_size)) {
+        LERROR << "Max metadata size " << metadata_max_size << " is too large.";
+        return false;
+    }
 
     // Validate and build the block device list.
     uint32_t logical_block_size = 0;
@@ -401,10 +405,15 @@
         // untouched to be compatible code that looks for an MBR. Thus we
         // start counting free sectors at sector 1, not 0.
         uint64_t free_area_start = LP_SECTOR_SIZE;
-        if (out.alignment || out.alignment_offset) {
-            free_area_start = AlignTo(free_area_start, out.alignment, out.alignment_offset);
+        bool ok;
+        if (out.alignment) {
+            ok = AlignTo(free_area_start, out.alignment, &free_area_start);
         } else {
-            free_area_start = AlignTo(free_area_start, logical_block_size);
+            ok = AlignTo(free_area_start, logical_block_size, &free_area_start);
+        }
+        if (!ok) {
+            LERROR << "Integer overflow computing free area start";
+            return false;
         }
         out.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
 
@@ -441,10 +450,15 @@
 
     // Compute the first free sector, factoring in alignment.
     uint64_t free_area_start = total_reserved;
+    bool ok;
     if (super.alignment || super.alignment_offset) {
-        free_area_start = AlignTo(free_area_start, super.alignment, super.alignment_offset);
+        ok = AlignTo(free_area_start, super.alignment, &free_area_start);
     } else {
-        free_area_start = AlignTo(free_area_start, logical_block_size);
+        ok = AlignTo(free_area_start, logical_block_size, &free_area_start);
+    }
+    if (!ok) {
+        LERROR << "Integer overflow computing free area start";
+        return false;
     }
     super.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
 
@@ -544,7 +558,11 @@
         const Interval& current = extents[i];
         DCHECK(previous.device_index == current.device_index);
 
-        uint64_t aligned = AlignSector(block_devices_[current.device_index], previous.end);
+        uint64_t aligned;
+        if (!AlignSector(block_devices_[current.device_index], previous.end, &aligned)) {
+            LERROR << "Sector " << previous.end << " caused integer overflow.";
+            continue;
+        }
         if (aligned >= current.start) {
             // There is no gap between these two extents, try the next one.
             // Note that we check with >= instead of >, since alignment may
@@ -730,7 +748,10 @@
     // Choose an aligned sector for the midpoint. This could lead to one half
     // being slightly larger than the other, but this will not restrict the
     // size of partitions (it might lead to one extra extent if "B" overflows).
-    midpoint = AlignSector(super, midpoint);
+    if (!AlignSector(super, midpoint, &midpoint)) {
+        LERROR << "Unexpected integer overflow aligning midpoint " << midpoint;
+        return free_list;
+    }
 
     std::vector<Interval> first_half;
     std::vector<Interval> second_half;
@@ -768,7 +789,11 @@
     // If the sector ends where the next aligned chunk begins, then there's
     // no missing gap to try and allocate.
     const auto& block_device = block_devices_[extent->device_index()];
-    uint64_t next_aligned_sector = AlignSector(block_device, extent->end_sector());
+    uint64_t next_aligned_sector;
+    if (!AlignSector(block_device, extent->end_sector(), &next_aligned_sector)) {
+        LERROR << "Integer overflow aligning sector " << extent->end_sector();
+        return nullptr;
+    }
     if (extent->end_sector() == next_aligned_sector) {
         return nullptr;
     }
@@ -925,13 +950,19 @@
     return size;
 }
 
-uint64_t MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device,
-                                      uint64_t sector) const {
+bool MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device, uint64_t sector,
+                                  uint64_t* out) const {
     // Note: when reading alignment info from the Kernel, we don't assume it
     // is aligned to the sector size, so we round up to the nearest sector.
     uint64_t lba = sector * LP_SECTOR_SIZE;
-    uint64_t aligned = AlignTo(lba, block_device.alignment, block_device.alignment_offset);
-    return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
+    if (!AlignTo(lba, block_device.alignment, out)) {
+        return false;
+    }
+    if (!AlignTo(*out, LP_SECTOR_SIZE, out)) {
+        return false;
+    }
+    *out /= LP_SECTOR_SIZE;
+    return true;
 }
 
 bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,
@@ -1005,7 +1036,12 @@
 bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size,
                                       const std::vector<Interval>& free_region_hint) {
     // Align the space needed up to the nearest sector.
-    uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
+    uint64_t aligned_size;
+    if (!AlignTo(requested_size, geometry_.logical_block_size, &aligned_size)) {
+        LERROR << "Cannot resize partition " << partition->name() << " to " << requested_size
+               << " bytes; integer overflow.";
+        return false;
+    }
     uint64_t old_size = partition->size();
 
     if (!ValidatePartitionSizeChange(partition, old_size, aligned_size, false)) {
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 977ebe3..1a3250a 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -174,7 +174,7 @@
     ASSERT_NE(exported, nullptr);
     super_device = GetMetadataSuperBlockDevice(*exported.get());
     ASSERT_NE(super_device, nullptr);
-    EXPECT_EQ(super_device->first_logical_sector, 1472);
+    EXPECT_EQ(super_device->first_logical_sector, 1536);
 
     // Alignment offset without alignment doesn't mean anything.
     device_info.alignment = 0;
@@ -190,7 +190,7 @@
     ASSERT_NE(exported, nullptr);
     super_device = GetMetadataSuperBlockDevice(*exported.get());
     ASSERT_NE(super_device, nullptr);
-    EXPECT_EQ(super_device->first_logical_sector, 174);
+    EXPECT_EQ(super_device->first_logical_sector, 168);
 
     // Test a small alignment with no alignment offset.
     device_info.alignment = 11 * 1024;
@@ -200,7 +200,7 @@
     ASSERT_NE(exported, nullptr);
     super_device = GetMetadataSuperBlockDevice(*exported.get());
     ASSERT_NE(super_device, nullptr);
-    EXPECT_EQ(super_device->first_logical_sector, 160);
+    EXPECT_EQ(super_device->first_logical_sector, 154);
 }
 
 TEST_F(BuilderTest, InternalPartitionAlignment) {
@@ -228,13 +228,14 @@
         ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
         EXPECT_EQ(extent.num_sectors, 80);
 
+        uint64_t aligned_lba;
         uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
-        uint64_t aligned_lba = AlignTo(lba, device_info.alignment, device_info.alignment_offset);
+        ASSERT_TRUE(AlignTo(lba, device_info.alignment, &aligned_lba));
         EXPECT_EQ(lba, aligned_lba);
     }
 
     // Sanity check one extent.
-    EXPECT_EQ(exported->extents.back().target_data, 3008);
+    EXPECT_EQ(exported->extents.back().target_data, 3072);
 }
 
 TEST_F(BuilderTest, UseAllDiskSpace) {
@@ -652,7 +653,7 @@
     };
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(partitions, "system_a", 65536, 2);
     ASSERT_NE(builder, nullptr);
-    EXPECT_EQ(builder->AllocatableSpace(), 467238912);
+    EXPECT_EQ(builder->AllocatableSpace(), 467402752);
 
     // Create a partition that spans 3 devices.
     Partition* p = builder->AddPartition("system_a", 0);
@@ -675,17 +676,17 @@
     EXPECT_EQ(metadata->block_devices[2].alignment, 786432);
     EXPECT_EQ(metadata->block_devices[2].alignment_offset, 753664);
     ASSERT_EQ(metadata->extents.size(), 3);
-    EXPECT_EQ(metadata->extents[0].num_sectors, 522304);
+    EXPECT_EQ(metadata->extents[0].num_sectors, 522752);
     EXPECT_EQ(metadata->extents[0].target_type, LP_TARGET_TYPE_LINEAR);
-    EXPECT_EQ(metadata->extents[0].target_data, 1984);
+    EXPECT_EQ(metadata->extents[0].target_data, 1536);
     EXPECT_EQ(metadata->extents[0].target_source, 0);
-    EXPECT_EQ(metadata->extents[1].num_sectors, 260672);
+    EXPECT_EQ(metadata->extents[1].num_sectors, 260608);
     EXPECT_EQ(metadata->extents[1].target_type, LP_TARGET_TYPE_LINEAR);
-    EXPECT_EQ(metadata->extents[1].target_data, 1472);
+    EXPECT_EQ(metadata->extents[1].target_data, 1536);
     EXPECT_EQ(metadata->extents[1].target_source, 1);
-    EXPECT_EQ(metadata->extents[2].num_sectors, 129088);
+    EXPECT_EQ(metadata->extents[2].num_sectors, 128704);
     EXPECT_EQ(metadata->extents[2].target_type, LP_TARGET_TYPE_LINEAR);
-    EXPECT_EQ(metadata->extents[2].target_data, 1472);
+    EXPECT_EQ(metadata->extents[2].target_data, 1536);
     EXPECT_EQ(metadata->extents[2].target_source, 2);
 }
 
@@ -1019,3 +1020,49 @@
     EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 15}));
     EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 10}));
 }
+
+TEST_F(BuilderTest, AlignFreeRegion) {
+    BlockDeviceInfo super("super", 8_GiB, 786432, 0, 4096);
+    std::vector<BlockDeviceInfo> block_devices = {super};
+
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2);
+    ASSERT_NE(builder, nullptr);
+
+    Partition* p = builder->AddPartition("system", "default", 0);
+    ASSERT_NE(p, nullptr);
+    ASSERT_TRUE(builder->AddLinearExtent(p, "super", 64, (super.alignment + 4096) / 512));
+
+    p = builder->AddPartition("vendor", "default", 0);
+    ASSERT_NE(p, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(p, 2_GiB));
+
+    const auto& extents = p->extents();
+    ASSERT_EQ(extents.size(), 2);
+
+    LinearExtent* e1 = extents[0]->AsLinearExtent();
+    ASSERT_NE(e1, nullptr);
+    LinearExtent* e2 = extents[1]->AsLinearExtent();
+    ASSERT_NE(e2, nullptr);
+
+    // The misaligned partition starting at sector 1544 should not cause any
+    // overlap with previous extents. We should see vendor punch a hole where
+    // "system" is, extending the hole up to the next aligned block.
+    EXPECT_EQ(e1->physical_sector(), 1536);
+    EXPECT_EQ(e1->end_sector(), 1544);
+    EXPECT_EQ(e2->physical_sector(), 3072);
+    EXPECT_EQ(e2->end_sector(), 4197368);
+}
+
+TEST_F(BuilderTest, ResizeOverflow) {
+    BlockDeviceInfo super("super", 8_GiB, 786432, 229376, 4096);
+    std::vector<BlockDeviceInfo> block_devices = {super};
+
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_TRUE(builder->AddGroup("group", 0));
+
+    Partition* p = builder->AddPartition("system", "default", 0);
+    ASSERT_NE(p, nullptr);
+    ASSERT_FALSE(builder->ResizePartition(p, 18446744073709551615ULL));
+}
diff --git a/fs_mgr/liblp/device_test.cpp b/fs_mgr/liblp/device_test.cpp
index 99bff6e..6af9d94 100644
--- a/fs_mgr/liblp/device_test.cpp
+++ b/fs_mgr/liblp/device_test.cpp
@@ -50,16 +50,7 @@
     // Sanity check that the device doesn't give us some weird inefficient
     // alignment.
     EXPECT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
-    EXPECT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
-    EXPECT_LE(device_info.alignment_offset, INT_MAX);
     EXPECT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
-
-    // Having an alignment offset > alignment doesn't really make sense.
-    EXPECT_LT(device_info.alignment_offset, device_info.alignment);
-
-    if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false)) {
-        EXPECT_EQ(device_info.alignment_offset, 0);
-    }
 }
 
 TEST_F(DeviceTest, ReadSuperPartitionCurrentSlot) {
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index bd39150..732dbea 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -359,7 +359,7 @@
     bool GrowPartition(Partition* partition, uint64_t aligned_size,
                        const std::vector<Interval>& free_region_hint);
     void ShrinkPartition(Partition* partition, uint64_t aligned_size);
-    uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const;
+    bool AlignSector(const LpMetadataBlockDevice& device, uint64_t sector, uint64_t* out) const;
     uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
     bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);
     bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 0661769..c4fe3ed 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -21,6 +21,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <limits>
 #include <string>
 #include <string_view>
 
@@ -66,27 +67,26 @@
 void SHA256(const void* data, size_t length, uint8_t out[32]);
 
 // Align |base| such that it is evenly divisible by |alignment|, which does not
-// have to be a power of two.
-constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment) {
+// have to be a power of two. Return false on overflow.
+template <typename T>
+bool AlignTo(T base, uint32_t alignment, T* out) {
+    static_assert(std::numeric_limits<T>::is_integer);
+    static_assert(!std::numeric_limits<T>::is_signed);
     if (!alignment) {
-        return base;
+        *out = base;
+        return true;
     }
-    uint64_t remainder = base % alignment;
+    T remainder = base % alignment;
     if (remainder == 0) {
-        return base;
+        *out = base;
+        return true;
     }
-    return base + (alignment - remainder);
-}
-
-// Same as the above |AlignTo|, except that |base| is only aligned when added to
-// |alignment_offset|.
-constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment, uint32_t alignment_offset) {
-    uint64_t aligned = AlignTo(base, alignment) + alignment_offset;
-    if (aligned - alignment >= base) {
-        // We overaligned (base < alignment_offset).
-        return aligned - alignment;
+    T to_add = alignment - remainder;
+    if (to_add > std::numeric_limits<T>::max() - base) {
+        return false;
     }
-    return aligned;
+    *out = base + to_add;
+    return true;
 }
 
 // Update names from C++ strings.
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index cac3989..fc90872 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <optional>
+
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
@@ -58,15 +60,28 @@
     EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), backup_start + 16384 * 0);
 }
 
+std::optional<uint64_t> AlignTo(uint64_t base, uint32_t alignment) {
+    uint64_t r;
+    if (!AlignTo(base, alignment, &r)) {
+        return {};
+    }
+    return {r};
+}
+
 TEST(liblp, AlignTo) {
-    EXPECT_EQ(AlignTo(37, 0), 37);
-    EXPECT_EQ(AlignTo(1024, 1024), 1024);
-    EXPECT_EQ(AlignTo(555, 1024), 1024);
-    EXPECT_EQ(AlignTo(555, 1000), 1000);
-    EXPECT_EQ(AlignTo(0, 1024), 0);
-    EXPECT_EQ(AlignTo(54, 32, 30), 62);
-    EXPECT_EQ(AlignTo(32, 32, 30), 62);
-    EXPECT_EQ(AlignTo(17, 32, 30), 30);
+    EXPECT_EQ(AlignTo(37, 0), std::optional<uint64_t>(37));
+    EXPECT_EQ(AlignTo(1024, 1024), std::optional<uint64_t>(1024));
+    EXPECT_EQ(AlignTo(555, 1024), std::optional<uint64_t>(1024));
+    EXPECT_EQ(AlignTo(555, 1000), std::optional<uint64_t>(1000));
+    EXPECT_EQ(AlignTo(0, 1024), std::optional<uint64_t>(0));
+    EXPECT_EQ(AlignTo(54, 32), std::optional<uint64_t>(64));
+    EXPECT_EQ(AlignTo(32, 32), std::optional<uint64_t>(32));
+    EXPECT_EQ(AlignTo(17, 32), std::optional<uint64_t>(32));
+
+    auto u32limit = std::numeric_limits<uint32_t>::max();
+    auto u64limit = std::numeric_limits<uint64_t>::max();
+    EXPECT_EQ(AlignTo(u64limit - u32limit + 1, u32limit), std::optional<uint64_t>{u64limit});
+    EXPECT_EQ(AlignTo(std::numeric_limits<uint64_t>::max(), 2), std::optional<uint64_t>{});
 }
 
 TEST(liblp, GetPartitionSlotSuffix) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 384595d..e916693 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -100,6 +100,7 @@
 
 cc_library_static {
     name: "libsnapshot_init",
+    native_coverage : true,
     defaults: ["libsnapshot_defaults"],
     srcs: [":libsnapshot_sources"],
     recovery_available: true,
@@ -160,23 +161,23 @@
         "snapshot_test.cpp",
     ],
     shared_libs: [
-        "android.hardware.boot@1.0",
-        "android.hardware.boot@1.1",
         "libbinder",
         "libcrypto",
         "libhidlbase",
         "libprotobuf-cpp-lite",
-        "libsparse",
         "libutils",
         "libz",
     ],
     static_libs: [
+        "android.hardware.boot@1.0",
+        "android.hardware.boot@1.1",
         "libfs_mgr",
         "libgsi",
         "libgmock",
         "liblp",
         "libsnapshot",
         "libsnapshot_test_helpers",
+        "libsparse",
     ],
     header_libs: [
         "libstorage_literals_headers",
@@ -244,3 +245,56 @@
     ],
     gtest: false,
 }
+
+cc_fuzz {
+    name: "libsnapshot_fuzzer",
+
+    // TODO(b/154633114): make host supported.
+    // host_supported: true,
+
+    native_coverage : true,
+    srcs: [
+        // Compile the protobuf definition again with type full.
+        "android/snapshot/snapshot_fuzz.proto",
+        "update_engine/update_metadata.proto",
+        "fuzz_utils.cpp",
+        "snapshot_fuzz.cpp",
+        "snapshot_fuzz_utils.cpp",
+
+        // Compile libsnapshot sources directly to avoid dependency
+        // to update_metadata-protos
+        ":libsnapshot_sources",
+    ],
+    static_libs: [
+        "libbase",
+        "libcrypto_static",
+        "libcutils",
+        "libext2_uuid",
+        "libext4_utils",
+        "libfstab",
+        "libfs_mgr",
+        "libgtest", // from libsnapshot_test_helpers
+        "libgmock", // from libsnapshot_test_helpers
+        "liblog",
+        "liblp",
+        "libsnapshot_test_helpers",
+        "libprotobuf-mutator",
+    ],
+    header_libs: [
+        "libfiemap_headers",
+        "libstorage_literals_headers",
+    ],
+    proto: {
+        type: "full",
+        canonical_path_from_root: false,
+        local_include_dirs: ["."],
+    },
+
+    fuzz_config: {
+        cc: ["android-virtual-ab+bugs@google.com"],
+        componentid: 30545,
+        hotlists: ["1646452"],
+        fuzz_on_haiku_host: false,
+        fuzz_on_haiku_device: true,
+    },
+}
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
new file mode 100644
index 0000000..91fbb60
--- /dev/null
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
@@ -0,0 +1,103 @@
+// 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.
+
+syntax = "proto3";
+package android.snapshot;
+
+import "update_engine/update_metadata.proto";
+
+// Controls the behavior of IDeviceInfo.
+// Next: 6
+message FuzzDeviceInfoData {
+    bool slot_suffix_is_a = 1;
+    bool is_overlayfs_setup = 2;
+    bool allow_set_boot_control_merge_status = 3;
+    bool allow_set_slot_as_unbootable = 4;
+    bool is_recovery = 5;
+}
+
+// Controls the behavior of the test SnapshotManager.
+// Next: 2
+message FuzzSnapshotManagerData {
+    bool is_local_image_manager = 1;
+}
+
+// A simplified version of CreateLogicalPartitionParams for fuzzing.
+// Next: 9
+message CreateLogicalPartitionParamsProto {
+    bool use_correct_super = 1;
+    string block_device = 2;
+    bool has_metadata_slot = 3;
+    uint32 metadata_slot = 4;
+    string partition_name = 5;
+    bool force_writable = 6;
+    int64 timeout_millis = 7;
+    string device_name = 8;
+}
+
+// Mimics the API of ISnapshotManager. Defines one action on the snapshot
+// manager.
+// Next: 18
+message SnapshotManagerActionProto {
+    message NoArgs {}
+    message ProcessUpdateStateArgs {
+        bool has_before_cancel = 1;
+        bool fail_before_cancel = 2;
+    }
+    message CreateLogicalAndSnapshotPartitionsArgs {
+        bool use_correct_super = 1;
+        string super = 2;
+        int64 timeout_millis = 3;
+    }
+    message RecoveryCreateSnapshotDevicesArgs {
+        bool has_metadata_device_object = 1;
+        bool metadata_mounted = 2;
+    }
+    oneof value {
+        NoArgs begin_update = 1;
+        NoArgs cancel_update = 2;
+        bool finished_snapshot_writes = 3;
+        NoArgs initiate_merge = 4;
+        ProcessUpdateStateArgs process_update_state = 5;
+        bool get_update_state = 6;
+        chromeos_update_engine.DeltaArchiveManifest create_update_snapshots = 7;
+        CreateLogicalPartitionParamsProto map_update_snapshot = 8;
+        string unmap_update_snapshot = 9;
+        NoArgs need_snapshots_in_first_stage_mount = 10;
+        CreateLogicalAndSnapshotPartitionsArgs create_logical_and_snapshot_partitions = 11;
+        bool handle_imminent_data_wipe = 12;
+        NoArgs recovery_create_snapshot_devices = 13;
+        RecoveryCreateSnapshotDevicesArgs recovery_create_snapshot_devices_with_metadata = 14;
+        NoArgs dump = 15;
+        NoArgs ensure_metadata_mounted = 16;
+        NoArgs get_snapshot_merge_stats_instance = 17;
+    }
+}
+
+// Includes all data that needs to be fuzzed.
+message SnapshotFuzzData {
+    FuzzDeviceInfoData device_info_data = 1;
+    FuzzSnapshotManagerData manager_data = 2;
+
+    // If true:
+    // - if super_data is empty, create empty super partition metadata.
+    // - otherwise, create super partition metadata accordingly.
+    // If false, no valid super partition metadata (it is zeroed)
+    bool is_super_metadata_valid = 3;
+    chromeos_update_engine.DeltaArchiveManifest super_data = 4;
+
+    // More data used to prep the test before running actions.
+    reserved 5 to 9999;
+    repeated SnapshotManagerActionProto actions = 10000;
+}
diff --git a/fs_mgr/libsnapshot/fuzz.sh b/fs_mgr/libsnapshot/fuzz.sh
new file mode 100755
index 0000000..2910129
--- /dev/null
+++ b/fs_mgr/libsnapshot/fuzz.sh
@@ -0,0 +1,88 @@
+#!/bin/bash
+PROJECT_PATH=system/core/fs_mgr/libsnapshot
+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_GCOV_DIR=/data/local/tmp/${FUZZ_TARGET}/gcov
+HOST_SCRATCH_DIR=/tmp/${FUZZ_TARGET}
+GCOV_TOOL=${HOST_SCRATCH_DIR}/llvm-gcov
+
+build_normal() (
+    pushd $(gettop)
+    NATIVE_COVERAGE="" NATIVE_LINE_COVERAGE="" COVERAGE_PATHS="" m ${FUZZ_TARGET}
+    ret=$?
+    popd
+    return ${ret}
+)
+
+build_cov() {
+    pushd $(gettop)
+    NATIVE_COVERAGE="true" NATIVE_LINE_COVERAGE="true" COVERAGE_PATHS="${PROJECT_PATH}" m ${FUZZ_TARGET}
+    ret=$?
+    popd
+    return ${ret}
+}
+
+prepare_device() {
+    adb root && adb remount &&
+    adb shell mkdir -p ${DEVICE_CORPSE_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}
+}
+
+prepare_host() {
+    which lcov || {
+        echo "please run:";
+        echo "   sudo apt-get install lcov ";
+        return 1;
+    }
+    rm -rf ${HOST_SCRATCH_DIR} &&
+    mkdir -p ${HOST_SCRATCH_DIR}
+}
+
+# run_snapshot_fuzz -runs=10000
+generate_corpus() {
+    [[ "$@" ]] || { echo "run with -runs=X"; return 1; }
+
+    prepare_device &&
+    build_normal &&
+    push_binary &&
+    adb shell ${FUZZ_BINARY} "$@" ${DEVICE_CORPSE_DIR}
+}
+
+run_snapshot_fuzz() {
+    prepare_device &&
+    build_cov &&
+    push_binary &&
+    adb shell GCOV_PREFIX=${DEVICE_GCOV_DIR} GCOV_PREFIX_STRIP=3 \
+        ${FUZZ_BINARY} \
+        -runs=0 \
+        ${DEVICE_CORPSE_DIR}
+}
+
+show_fuzz_result() {
+    prepare_host &&
+    unzip -o -j -d ${HOST_SCRATCH_DIR} ${ANDROID_PRODUCT_OUT}/coverage/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/${FUZZ_TARGET}.zip &&
+    adb shell find ${DEVICE_GCOV_DIR} -type f | xargs -I {} adb pull {} ${HOST_SCRATCH_DIR} &&
+    ls ${HOST_SCRATCH_DIR} &&
+    cat > ${GCOV_TOOL} <<< '
+#!/bin/bash
+exec llvm-cov gcov "$@"
+' &&
+    chmod +x ${GCOV_TOOL} &&
+    lcov --directory ${HOST_SCRATCH_DIR} --base-directory $(gettop) --gcov-tool ${GCOV_TOOL} --capture -o ${HOST_SCRATCH_DIR}/report.cov &&
+    genhtml ${HOST_SCRATCH_DIR}/report.cov -o ${HOST_SCRATCH_DIR}/html &&
+    echo file://$(realpath ${HOST_SCRATCH_DIR}/html/index.html)
+}
+
+# run_snapshot_fuzz -runs=10000
+run_snapshot_fuzz_all() {
+    generate_corpse "$@" &&
+    run_snapshot_fuzz &&
+    show_fuzz_result
+}
diff --git a/fs_mgr/libsnapshot/fuzz_utils.cpp b/fs_mgr/libsnapshot/fuzz_utils.cpp
new file mode 100644
index 0000000..0263f7e
--- /dev/null
+++ b/fs_mgr/libsnapshot/fuzz_utils.cpp
@@ -0,0 +1,38 @@
+// 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 "fuzz_utils.h"
+
+#include <android-base/logging.h>
+
+namespace android::fuzz {
+
+void CheckInternal(bool value, std::string_view msg) {
+    CHECK(value) << msg;
+}
+
+const google::protobuf::OneofDescriptor* GetProtoValueDescriptor(
+        const google::protobuf::Descriptor* action_desc) {
+    CHECK(action_desc);
+    CHECK(action_desc->oneof_decl_count() == 1)
+            << action_desc->oneof_decl_count() << " oneof fields found in " << action_desc->name()
+            << "; only one is expected.";
+    auto* oneof_value_desc = action_desc->oneof_decl(0);
+    CHECK(oneof_value_desc);
+    CHECK(oneof_value_desc->name() == "value")
+            << "oneof field has name " << oneof_value_desc->name();
+    return oneof_value_desc;
+}
+
+}  // namespace android::fuzz
diff --git a/fs_mgr/libsnapshot/fuzz_utils.h b/fs_mgr/libsnapshot/fuzz_utils.h
new file mode 100644
index 0000000..4dc6cdc
--- /dev/null
+++ b/fs_mgr/libsnapshot/fuzz_utils.h
@@ -0,0 +1,265 @@
+// 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 <map>
+#include <string>
+#include <string_view>
+
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>
+
+// Utilities for using a protobuf definition to fuzz APIs in a class.
+// Terms:
+// The "fuzzed class" is the C++ class definition whose functions are fuzzed.
+// The "fuzzed object" is an instantiated object of the fuzzed class. It is
+//   typically created and destroyed for each test run.
+// An "action" is an operation on the fuzzed object that may mutate its state.
+//   This typically involves one function call into the fuzzed object.
+
+namespace android::fuzz {
+
+// CHECK(value) << msg
+void CheckInternal(bool value, std::string_view msg);
+
+// Get the oneof descriptor inside Action
+const google::protobuf::OneofDescriptor* GetProtoValueDescriptor(
+        const google::protobuf::Descriptor* action_desc);
+
+template <typename Class>
+using FunctionMapImpl =
+        std::map<int, std::function<void(Class*, const google::protobuf::Message& action_proto,
+                                         const google::protobuf::FieldDescriptor* field_desc)>>;
+
+template <typename Class>
+class FunctionMap : public FunctionMapImpl<Class> {
+  public:
+    void CheckEmplace(typename FunctionMapImpl<Class>::key_type key,
+                      typename FunctionMapImpl<Class>::mapped_type&& value) {
+        auto [it, inserted] = this->emplace(key, std::move(value));
+        CheckInternal(inserted,
+                      "Multiple implementation registered for tag number " + std::to_string(key));
+    }
+};
+
+template <typename Action>
+int CheckConsistency() {
+    const auto* function_map = Action::GetFunctionMap();
+    const auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor());
+
+    for (int field_index = 0; field_index < action_value_desc->field_count(); ++field_index) {
+        const auto* field_desc = action_value_desc->field(field_index);
+        CheckInternal(function_map->find(field_desc->number()) != function_map->end(),
+                      "Missing impl for function " + field_desc->camelcase_name());
+    }
+    return 0;
+}
+
+template <typename Action>
+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;
+    }
+
+    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);
+    CheckInternal(it != map.end(), "Missing impl for function " + field_desc->camelcase_name());
+    const auto& func = it->second;
+    func(module, action_proto, field_desc);
+}
+
+template <typename Action>
+void ExecuteAllActionProtos(
+        typename Action::Class* module,
+        const google::protobuf::RepeatedPtrField<typename Action::Proto>& action_protos) {
+    for (const auto& proto : action_protos) {
+        ExecuteActionProto<Action>(module, proto);
+    }
+}
+
+// Safely cast message to T. Returns a pointer to message if cast successfully, otherwise nullptr.
+template <typename T>
+const T* SafeCast(const google::protobuf::Message& message) {
+    if (message.GetDescriptor() != T::GetDescriptor()) {
+        return nullptr;
+    }
+    return static_cast<const T*>(&message);
+}
+
+// Cast message to const T&. Abort if type mismatch.
+template <typename T>
+const T& CheckedCast(const google::protobuf::Message& message) {
+    const auto* ptr = SafeCast<T>(message);
+    CheckInternal(ptr, "Cannot cast " + message.GetDescriptor()->name() + " to " +
+                               T::GetDescriptor()->name());
+    return *ptr;
+}
+
+// A templated way to a primitive field from a message using reflection.
+template <typename T>
+struct PrimitiveGetter;
+#define FUZZ_DEFINE_PRIMITIVE_GETTER(type, func_name)                              \
+    template <>                                                                    \
+    struct PrimitiveGetter<type> {                                                 \
+        static constexpr const auto fp = &google::protobuf::Reflection::func_name; \
+    }
+
+FUZZ_DEFINE_PRIMITIVE_GETTER(bool, GetBool);
+FUZZ_DEFINE_PRIMITIVE_GETTER(uint32_t, GetUInt32);
+FUZZ_DEFINE_PRIMITIVE_GETTER(int32_t, GetInt32);
+FUZZ_DEFINE_PRIMITIVE_GETTER(uint64_t, GetUInt64);
+FUZZ_DEFINE_PRIMITIVE_GETTER(int64_t, GetInt64);
+FUZZ_DEFINE_PRIMITIVE_GETTER(double, GetDouble);
+FUZZ_DEFINE_PRIMITIVE_GETTER(float, GetFloat);
+
+// 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
+
+template <typename FuzzFunction, typename MessageProto>
+struct ActionPerfomer<
+        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) {
+        const MessageProto& arg = CheckedCast<std::remove_reference_t<MessageProto>>(
+                action_proto.GetReflection()->GetMessage(action_proto, field_desc));
+        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) {
+        Primitive arg = std::invoke(PrimitiveGetter<Primitive>::fp, action_proto.GetReflection(),
+                                    action_proto, field_desc);
+        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);
+    }
+};
+
+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) {
+        std::string scratch;
+        const std::string& arg = action_proto.GetReflection()->GetStringReference(
+                action_proto, field_desc, &scratch);
+        FuzzFunction::ImplBody(module, arg);
+    }
+};
+
+}  // namespace android::fuzz
+
+// Fuzz existing C++ class, ClassType, with a collection of functions under the name Action.
+//
+// Prerequisite: ActionProto must be defined in Protobuf to describe possible actions:
+// message FooActionProto {
+//     message NoArgs {}
+//     oneof value {
+//         bool do_foo = 1;
+//         NoArgs do_bar = 1;
+//     }
+// }
+// Use it to fuzz a C++ class Foo by doing the following:
+//   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)                                                            \
+    class Action {                                                                               \
+      public:                                                                                    \
+        using Proto = Action##Proto;                                                             \
+        using Class = ClassType;                                                                 \
+        using FunctionMap = android::fuzz::FunctionMap<Class>;                                   \
+        static FunctionMap* GetFunctionMap() {                                                   \
+            static Action::FunctionMap map;                                                      \
+            return &map;                                                                         \
+        }                                                                                        \
+        static void ExecuteAll(Class* module,                                                    \
+                               const google::protobuf::RepeatedPtrField<Proto>& action_protos) { \
+            [[maybe_unused]] static int consistent = android::fuzz::CheckConsistency<Action>();  \
+            android::fuzz::ExecuteAllActionProtos<Action>(module, action_protos);                \
+        }                                                                                        \
+    }
+
+#define FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) Action##_##FunctionName
+#define FUZZ_FUNCTION_TAG_NAME(FunctionName) k##FunctionName
+
+// Implement an action defined in protobuf. Example:
+// message FooActionProto {
+//     oneof value {
+//         bool do_foo = 1;
+//     }
+// }
+// class Foo { public: void DoAwesomeFoo(bool arg); };
+// FUZZ_OBJECT(FooAction, Foo);
+// 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, 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 {
+//     message NoArgs {}
+//     oneof value {
+//         NoArgs do_bar = 1;
+//     }
+// }
+// class Foo { public void DoBar(); };
+// FUZZ_OBJECT(FooAction, Foo);
+// 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(); }
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index 758d66c..cf0b085 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -43,6 +43,7 @@
                 (const std::string& super_device, const std::chrono::milliseconds& timeout_ms),
                 (override));
     MOCK_METHOD(bool, HandleImminentDataWipe, (const std::function<void()>& callback), (override));
+    MOCK_METHOD(bool, FinishMergeInRecovery, (), (override));
     MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices, (), (override));
     MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices,
                 (const std::unique_ptr<AutoDevice>& metadata_device), (override));
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index fff667e..2d6071f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -173,6 +173,7 @@
 
     // Map a snapshotted partition for OTA clients to write to. Write-protected regions are
     // determined previously in CreateSnapshots.
+    // |snapshot_path| must not be nullptr.
     virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
                                    std::string* snapshot_path) = 0;
 
@@ -201,6 +202,10 @@
     // optional callback fires periodically to query progress via GetUpdateState.
     virtual bool HandleImminentDataWipe(const std::function<void()>& callback = {}) = 0;
 
+    // Force a merge to complete in recovery. This is similar to HandleImminentDataWipe
+    // but does not expect a data wipe after.
+    virtual bool FinishMergeInRecovery() = 0;
+
     // This method is only allowed in recovery and is used as a helper to
     // initialize the snapshot devices as a requirement to mount a snapshotted
     // /system in recovery.
@@ -289,6 +294,7 @@
             const std::string& super_device,
             const std::chrono::milliseconds& timeout_ms = {}) override;
     bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;
+    bool FinishMergeInRecovery() override;
     CreateResult RecoveryCreateSnapshotDevices() override;
     CreateResult RecoveryCreateSnapshotDevices(
             const std::unique_ptr<AutoDevice>& metadata_device) override;
@@ -318,6 +324,7 @@
     friend class SnapshotUpdateTest;
     friend class FlashAfterUpdateTest;
     friend class LockTestConsumer;
+    friend class SnapshotFuzzEnv;
     friend struct AutoDeleteCowImage;
     friend struct AutoDeleteSnapshot;
     friend struct PartitionCowCreator;
@@ -573,11 +580,16 @@
     bool ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
                                       const std::function<bool()>& callback);
 
+    // Return device string of a mapped image, or if it is not available, the mapped image path.
+    bool GetMappedImageDeviceStringOrPath(const std::string& device_name,
+                                          std::string* device_string_or_mapped_path);
+
     std::string gsid_dir_;
     std::string metadata_dir_;
     std::unique_ptr<IDeviceInfo> device_;
     std::unique_ptr<IImageManager> images_;
     bool has_local_image_manager_ = false;
+    bool in_factory_data_reset_ = false;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index 9b2590c..9c82906 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -41,6 +41,7 @@
             const std::string& super_device,
             const std::chrono::milliseconds& timeout_ms = {}) override;
     bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;
+    bool FinishMergeInRecovery() override;
     CreateResult RecoveryCreateSnapshotDevices() override;
     CreateResult RecoveryCreateSnapshotDevices(
             const std::unique_ptr<AutoDevice>& metadata_device) override;
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index efdb59f..0df5664 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -181,6 +181,13 @@
     ret.snapshot_status.set_device_size(target_partition->size());
     ret.snapshot_status.set_snapshot_size(target_partition->size());
 
+    if (ret.snapshot_status.snapshot_size() == 0) {
+        LOG(INFO) << "Not creating snapshot for partition " << ret.snapshot_status.name();
+        ret.snapshot_status.set_cow_partition_size(0);
+        ret.snapshot_status.set_cow_file_size(0);
+        return ret;
+    }
+
     // Being the COW partition virtual, its size doesn't affect the storage
     // memory that will be occupied by the target.
     // The actual storage space is affected by the COW file, whose size depends
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index 526f874..adfb975 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -46,20 +46,20 @@
 };
 
 TEST_F(PartitionCowCreatorTest, IntersectSelf) {
-    constexpr uint64_t initial_size = 1_MiB;
-    constexpr uint64_t final_size = 40_KiB;
+    constexpr uint64_t super_size = 1_MiB;
+    constexpr uint64_t partition_size = 40_KiB;
 
-    auto builder_a = MetadataBuilder::New(initial_size, 1_KiB, 2);
+    auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
     ASSERT_NE(builder_a, nullptr);
     auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system_a, nullptr);
-    ASSERT_TRUE(builder_a->ResizePartition(system_a, final_size));
+    ASSERT_TRUE(builder_a->ResizePartition(system_a, partition_size));
 
-    auto builder_b = MetadataBuilder::New(initial_size, 1_KiB, 2);
+    auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
     ASSERT_NE(builder_b, nullptr);
     auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system_b, nullptr);
-    ASSERT_TRUE(builder_b->ResizePartition(system_b, final_size));
+    ASSERT_TRUE(builder_b->ResizePartition(system_b, partition_size));
 
     PartitionCowCreator creator{.target_metadata = builder_b.get(),
                                 .target_suffix = "_b",
@@ -68,8 +68,8 @@
                                 .current_suffix = "_a"};
     auto ret = creator.Run();
     ASSERT_TRUE(ret.has_value());
-    ASSERT_EQ(final_size, ret->snapshot_status.device_size());
-    ASSERT_EQ(final_size, ret->snapshot_status.snapshot_size());
+    ASSERT_EQ(partition_size, ret->snapshot_status.device_size());
+    ASSERT_EQ(partition_size, ret->snapshot_status.snapshot_size());
 }
 
 TEST_F(PartitionCowCreatorTest, Holes) {
@@ -118,20 +118,20 @@
     using RepeatedInstallOperationPtr = google::protobuf::RepeatedPtrField<InstallOperation>;
     using Extent = chromeos_update_engine::Extent;
 
-    constexpr uint64_t initial_size = 50_MiB;
-    constexpr uint64_t final_size = 40_MiB;
+    constexpr uint64_t super_size = 50_MiB;
+    constexpr uint64_t partition_size = 40_MiB;
 
-    auto builder_a = MetadataBuilder::New(initial_size, 1_KiB, 2);
+    auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
     ASSERT_NE(builder_a, nullptr);
     auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system_a, nullptr);
-    ASSERT_TRUE(builder_a->ResizePartition(system_a, final_size));
+    ASSERT_TRUE(builder_a->ResizePartition(system_a, partition_size));
 
-    auto builder_b = MetadataBuilder::New(initial_size, 1_KiB, 2);
+    auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
     ASSERT_NE(builder_b, nullptr);
     auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system_b, nullptr);
-    ASSERT_TRUE(builder_b->ResizePartition(system_b, final_size));
+    ASSERT_TRUE(builder_b->ResizePartition(system_b, partition_size));
 
     const uint64_t block_size = builder_b->logical_block_size();
     const uint64_t chunk_size = kSnapshotChunkSize * dm::kSectorSize;
@@ -197,6 +197,31 @@
     ASSERT_EQ(6 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
 }
 
+TEST_F(PartitionCowCreatorTest, Zero) {
+    constexpr uint64_t super_size = 1_MiB;
+    auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
+    ASSERT_NE(builder_a, nullptr);
+
+    auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
+    ASSERT_NE(builder_b, nullptr);
+    auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system_b, nullptr);
+
+    PartitionCowCreator creator{.target_metadata = builder_b.get(),
+                                .target_suffix = "_b",
+                                .target_partition = system_b,
+                                .current_metadata = builder_a.get(),
+                                .current_suffix = "_a",
+                                .operations = nullptr};
+
+    auto ret = creator.Run();
+
+    ASSERT_EQ(0u, ret->snapshot_status.device_size());
+    ASSERT_EQ(0u, ret->snapshot_status.snapshot_size());
+    ASSERT_EQ(0u, ret->snapshot_status.cow_file_size());
+    ASSERT_EQ(0u, ret->snapshot_status.cow_partition_size());
+}
+
 TEST(DmSnapshotInternals, CowSizeCalculator) {
     DmSnapCowSizeCalculator cc(512, 8);
     unsigned long int b;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index eafeea2..488009a 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -577,8 +577,16 @@
         return false;
     }
 
+    auto other_suffix = device_->GetOtherSlotSuffix();
+
     auto& dm = DeviceMapper::Instance();
     for (const auto& snapshot : snapshots) {
+        if (android::base::EndsWith(snapshot, other_suffix)) {
+            // Allow the merge to continue, but log this unexpected case.
+            LOG(ERROR) << "Unexpected snapshot found during merge: " << snapshot;
+            continue;
+        }
+
         // The device has to be mapped, since everything should be merged at
         // the same time. This is a fairly serious error. We could forcefully
         // map everything here, but it should have been mapped during first-
@@ -1008,6 +1016,15 @@
 }
 
 void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
+    // It's not possible to remove update state in recovery, so write an
+    // indicator that cleanup is needed on reboot. If a factory data reset
+    // was requested, it doesn't matter, everything will get wiped anyway.
+    // To make testing easier we consider a /data wipe as cleaned up.
+    if (device_->IsRecovery() && !in_factory_data_reset_) {
+        WriteUpdateState(lock, UpdateState::MergeCompleted);
+        return;
+    }
+
     RemoveAllUpdateState(lock);
 }
 
@@ -1674,7 +1691,7 @@
         return false;
     }
     std::string cow_device;
-    if (!dm.GetDeviceString(cow_name, &cow_device)) {
+    if (!GetMappedImageDeviceStringOrPath(cow_name, &cow_device)) {
         LOG(ERROR) << "Could not determine major/minor for: " << cow_name;
         return false;
     }
@@ -1771,7 +1788,7 @@
     // If the COW image exists, append it as the last extent.
     if (snapshot_status.cow_file_size() > 0) {
         std::string cow_image_device;
-        if (!dm.GetDeviceString(cow_image_name, &cow_image_device)) {
+        if (!GetMappedImageDeviceStringOrPath(cow_image_name, &cow_image_device)) {
             LOG(ERROR) << "Cannot determine major/minor for: " << cow_image_name;
             return false;
         }
@@ -2347,7 +2364,6 @@
         const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
     CHECK(lock);
 
-    auto& dm = DeviceMapper::Instance();
     CreateLogicalPartitionParams cow_params{
             .block_device = LP_METADATA_DEFAULT_PARTITION_NAME,
             .metadata = exported_target_metadata,
@@ -2372,7 +2388,7 @@
         }
 
         std::string cow_path;
-        if (!dm.GetDmDevicePathByName(cow_name, &cow_path)) {
+        if (!images_->GetMappedImageDevice(cow_name, &cow_path)) {
             LOG(ERROR) << "Cannot determine path for " << cow_name;
             return Return::Error();
         }
@@ -2528,7 +2544,43 @@
         }
         return true;
     };
-    if (!ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback)) {
+
+    in_factory_data_reset_ = true;
+    bool ok = ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback);
+    in_factory_data_reset_ = false;
+
+    if (!ok) {
+        return false;
+    }
+
+    // Nothing should be depending on partitions now, so unmap them all.
+    if (!UnmapAllPartitions()) {
+        LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
+    }
+    return true;
+}
+
+bool SnapshotManager::FinishMergeInRecovery() {
+    if (!device_->IsRecovery()) {
+        LOG(ERROR) << "Data wipes are only allowed in recovery.";
+        return false;
+    }
+
+    auto mount = EnsureMetadataMounted();
+    if (!mount || !mount->HasDevice()) {
+        return false;
+    }
+
+    auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+    auto super_path = device_->GetSuperDevice(slot_number);
+    if (!CreateLogicalAndSnapshotPartitions(super_path)) {
+        LOG(ERROR) << "Unable to map partitions to complete merge.";
+        return false;
+    }
+
+    UpdateState state = ProcessUpdateState();
+    if (state != UpdateState::MergeCompleted) {
+        LOG(ERROR) << "Merge returned unexpected status: " << state;
         return false;
     }
 
@@ -2689,5 +2741,24 @@
     return SnapshotMergeStats::GetInstance(*this);
 }
 
+bool SnapshotManager::GetMappedImageDeviceStringOrPath(const std::string& device_name,
+                                                       std::string* device_string_or_mapped_path) {
+    auto& dm = DeviceMapper::Instance();
+    // Try getting the device string if it is a device mapper device.
+    if (dm.GetState(device_name) != DmDeviceState::INVALID) {
+        return dm.GetDeviceString(device_name, device_string_or_mapped_path);
+    }
+
+    // Otherwise, get path from IImageManager.
+    if (!images_->GetMappedImageDevice(device_name, device_string_or_mapped_path)) {
+        return false;
+    }
+
+    LOG(WARNING) << "Calling GetMappedImageDevice with local image manager; device "
+                 << (device_string_or_mapped_path ? *device_string_or_mapped_path : "(nullptr)")
+                 << "may not be available in first stage init! ";
+    return true;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
new file mode 100644
index 0000000..421154d
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
@@ -0,0 +1,215 @@
+// 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 <stddef.h>
+#include <stdint.h>
+#include <sysexits.h>
+
+#include <functional>
+#include <sstream>
+#include <tuple>
+
+#include <android-base/logging.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::LogId;
+using android::base::LogSeverity;
+using android::base::SetLogger;
+using android::base::StderrLogger;
+using android::base::StdioLogger;
+using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fuzz::CheckedCast;
+using android::snapshot::SnapshotFuzzData;
+using android::snapshot::SnapshotFuzzEnv;
+using chromeos_update_engine::DeltaArchiveManifest;
+using google::protobuf::RepeatedPtrField;
+
+// Avoid linking to libgsi since it needs disk I/O.
+namespace android::gsi {
+bool IsGsiRunning() {
+    LOG(FATAL) << "Called IsGsiRunning";
+    __builtin_unreachable();
+}
+std::string GetDsuSlot(const std::string& install_dir) {
+    LOG(FATAL) << "Called GetDsuSlot(" << install_dir << ")";
+    __builtin_unreachable();
+}
+}  // namespace android::gsi
+
+namespace android::snapshot {
+
+const SnapshotFuzzData* current_data = nullptr;
+
+SnapshotFuzzEnv* GetSnapshotFuzzEnv();
+
+FUZZ_CLASS(ISnapshotManager, SnapshotManagerAction);
+
+using ProcessUpdateStateArgs = SnapshotManagerAction::Proto::ProcessUpdateStateArgs;
+using CreateLogicalAndSnapshotPartitionsArgs =
+        SnapshotManagerAction::Proto::CreateLogicalAndSnapshotPartitionsArgs;
+using RecoveryCreateSnapshotDevicesArgs =
+        SnapshotManagerAction::Proto::RecoveryCreateSnapshotDevicesArgs;
+
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, BeginUpdate);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, CancelUpdate);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, InitiateMerge);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, NeedSnapshotsInFirstStageMount);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, RecoveryCreateSnapshotDevices);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, EnsureMetadataMounted);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, GetSnapshotMergeStatsInstance);
+
+#define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ...) \
+    FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, snapshot, ##__VA_ARGS__)
+
+SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool wipe) {
+    (void)snapshot->FinishedSnapshotWrites(wipe);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, 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);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, bool has_progress_arg) {
+    double progress;
+    (void)snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool has_callback) {
+    std::function<void()> callback;
+    if (has_callback) {
+        callback = []() {};
+    }
+    (void)snapshot->HandleImminentDataWipe(callback);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(Dump) {
+    std::stringstream ss;
+    (void)snapshot->Dump(ss);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, const DeltaArchiveManifest& manifest) {
+    (void)snapshot->CreateUpdateSnapshots(manifest);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, const std::string& name) {
+    (void)snapshot->UnmapUpdateSnapshot(name);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions,
+                       const CreateLogicalAndSnapshotPartitionsArgs& args) {
+    const std::string* super;
+    if (args.use_correct_super()) {
+        super = &GetSnapshotFuzzEnv()->super();
+    } else {
+        super = &args.super();
+    }
+    (void)snapshot->CreateLogicalAndSnapshotPartitions(
+            *super, std::chrono::milliseconds(args.timeout_millis()));
+}
+
+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());
+    }
+    (void)snapshot->RecoveryCreateSnapshotDevices(device);
+}
+
+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()) {
+        params.block_device = GetSnapshotFuzzEnv()->super();
+    } else {
+        params.block_device = params_proto.block_device();
+    }
+    if (params_proto.has_metadata_slot()) {
+        params.metadata_slot = params_proto.metadata_slot();
+    }
+    params.partition_name = params_proto.partition_name();
+    params.force_writable = params_proto.force_writable();
+    params.timeout_ms = std::chrono::milliseconds(params_proto.timeout_millis());
+    params.device_name = params_proto.device_name();
+    params.partition_opener = partition_opener.get();
+    std::string path;
+    (void)snapshot->MapUpdateSnapshot(params, &path);
+}
+
+// During global init, log all messages to stdio. This is only done once.
+int AllowLoggingDuringGlobalInit() {
+    SetLogger(&StdioLogger);
+    return 0;
+}
+
+// Only log fatal messages during tests.
+void FatalOnlyLogger(LogId logid, LogSeverity severity, const char* tag, const char* file,
+                     unsigned int line, const char* message) {
+    if (severity == LogSeverity::FATAL) {
+        StderrLogger(logid, severity, tag, file, line, message);
+
+        // If test fails by a LOG(FATAL) or CHECK(), log the corpus. If it abort()'s, there's
+        // nothing else we can do.
+        StderrLogger(logid, severity, tag, __FILE__, __LINE__,
+                     "Attempting to dump current corpus:");
+        if (current_data == nullptr) {
+            StderrLogger(logid, severity, tag, __FILE__, __LINE__, "Current corpus is nullptr.");
+        } else {
+            std::string content;
+            if (!google::protobuf::TextFormat::PrintToString(*current_data, &content)) {
+                StderrLogger(logid, severity, tag, __FILE__, __LINE__,
+                             "Failed to print corpus to string.");
+            } else {
+                StderrLogger(logid, severity, tag, __FILE__, __LINE__, content.c_str());
+            }
+        }
+    }
+}
+// Stop logging (except fatal messages) after global initialization. This is only done once.
+int StopLoggingAfterGlobalInit() {
+    [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silincer;
+    SetLogger(&FatalOnlyLogger);
+    return 0;
+}
+
+SnapshotFuzzEnv* GetSnapshotFuzzEnv() {
+    [[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit();
+    static SnapshotFuzzEnv env;
+    [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
+    return &env;
+}
+
+}  // 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());
+}
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
new file mode 100644
index 0000000..8101d03
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
@@ -0,0 +1,314 @@
+// 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 <ftw.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sysexits.h>
+
+#include <chrono>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <libsnapshot/auto_device.h>
+#include <libsnapshot/snapshot.h>
+#include <storage_literals/storage_literals.h>
+
+#include "snapshot_fuzz_utils.h"
+#include "utility.h"
+
+// Prepends the errno string, but it is good enough.
+#ifndef PCHECK
+#define PCHECK(x) CHECK(x) << strerror(errno) << ": "
+#endif
+
+using namespace android::storage_literals;
+using namespace std::chrono_literals;
+using namespace std::string_literals;
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::base::WriteStringToFile;
+using android::dm::LoopControl;
+using android::fiemap::IImageManager;
+using android::fiemap::ImageManager;
+using android::fs_mgr::BlockDeviceInfo;
+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 FAKE_ROOT_NAME[] = "snapshot_fuzz";
+static const auto SUPER_IMAGE_SIZE = 16_MiB;
+static const auto FAKE_ROOT_SIZE = 64_MiB;
+
+namespace android::snapshot {
+
+bool Mkdir(const std::string& path) {
+    if (mkdir(path.c_str(), 0750) == -1 && errno != EEXIST) {
+        PLOG(ERROR) << "Cannot create " << path;
+        return false;
+    }
+    return true;
+}
+
+bool RmdirRecursive(const std::string& path) {
+    auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int {
+        switch (file_type) {
+            case FTW_D:
+            case FTW_DP:
+            case FTW_DNR:
+                if (rmdir(child) == -1) {
+                    PLOG(ERROR) << "rmdir " << child;
+                    return -1;
+                }
+                return 0;
+            case FTW_NS:
+            default:
+                if (rmdir(child) != -1) break;
+                [[fallthrough]];
+            case FTW_F:
+            case FTW_SL:
+            case FTW_SLN:
+                if (unlink(child) == -1) {
+                    PLOG(ERROR) << "unlink " << child;
+                    return -1;
+                }
+                return 0;
+        }
+        return 0;
+    };
+
+    return nftw(path.c_str(), callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) == 0;
+}
+
+class AutoDeleteDir : public AutoDevice {
+  public:
+    static std::unique_ptr<AutoDeleteDir> New(const std::string& path) {
+        if (!Mkdir(path)) {
+            return std::unique_ptr<AutoDeleteDir>(new AutoDeleteDir(""));
+        }
+        return std::unique_ptr<AutoDeleteDir>(new AutoDeleteDir(path));
+    }
+    ~AutoDeleteDir() {
+        if (!HasDevice()) return;
+        if (rmdir(name_.c_str()) == -1) {
+            PLOG(ERROR) << "Cannot remove " << name_;
+        }
+    }
+
+  private:
+    AutoDeleteDir(const std::string& path) : AutoDevice(path) {}
+};
+
+class AutoUnmount : public AutoDevice {
+  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) {
+            PLOG(ERROR) << "Cannot mount " << path;
+            return std::unique_ptr<AutoUnmount>(new AutoUnmount(""));
+        }
+        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) {}
+};
+
+// 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);
+        if (!ret->auto_umount_mount_point_->HasDevice()) {
+            return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+        }
+        // tmp_path() and persist_path does not need to be deleted upon destruction, hence it is
+        // not wrapped with AutoDeleteDir.
+        if (!Mkdir(ret->tmp_path())) {
+            return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+        }
+        if (!Mkdir(ret->persist_path())) {
+            return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+        }
+        return ret;
+    }
+    // Return the temporary scratch directory.
+    std::string tmp_path() const {
+        CHECK(HasDevice());
+        return mount_path() + "/tmp";
+    }
+    // Return the temporary scratch directory.
+    std::string persist_path() const {
+        CHECK(HasDevice());
+        return mount_path() + "/persist";
+    }
+    // Delete all contents in tmp_path() and start over. tmp_path() itself is re-created.
+    void CheckSoftReset() {
+        PCHECK(RmdirRecursive(tmp_path()));
+        PCHECK(Mkdir(tmp_path()));
+    }
+
+  private:
+    AutoMemBasedDir(const std::string& name) : AutoDevice(name) {}
+    std::string mount_path() const {
+        CHECK(HasDevice());
+        return MNT_DIR + "/"s + name_;
+    }
+    std::unique_ptr<AutoDeleteDir> auto_delete_mount_dir_;
+    std::unique_ptr<AutoUnmount> auto_umount_mount_point_;
+};
+
+SnapshotFuzzEnv::SnapshotFuzzEnv() {
+    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_);
+}
+
+SnapshotFuzzEnv::~SnapshotFuzzEnv() = default;
+
+void CheckZeroFill(const std::string& file, size_t size) {
+    std::string zeros(size, '\0');
+    PCHECK(WriteStringToFile(zeros, file)) << "Cannot write zeros to " << file;
+}
+
+void SnapshotFuzzEnv::CheckSoftReset() {
+    fake_root_->CheckSoftReset();
+    CheckZeroFill(super(), SUPER_IMAGE_SIZE);
+}
+
+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));
+    PCHECK(Mkdir(metadata_dir));
+    PCHECK(Mkdir(data_dir));
+    return ImageManager::Open(metadata_dir, data_dir);
+}
+
+// Helper to create a loop device for a file.
+static void CheckCreateLoopDevice(LoopControl* control, const std::string& file,
+                                  const std::chrono::milliseconds& timeout_ms, std::string* path) {
+    static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
+    android::base::unique_fd file_fd(open(file.c_str(), kOpenFlags));
+    PCHECK(file_fd >= 0) << "Could not open file: " << file;
+    CHECK(control->Attach(file_fd, timeout_ms, path))
+            << "Could not create loop device for: " << file;
+}
+
+class AutoDetachLoopDevice : public AutoDevice {
+  public:
+    AutoDetachLoopDevice(LoopControl* control, const std::string& device)
+        : AutoDevice(device), control_(control) {}
+    ~AutoDetachLoopDevice() { control_->Detach(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);
+
+    return std::make_unique<AutoDetachLoopDevice>(control, *fake_super);
+}
+
+std::unique_ptr<ISnapshotManager> SnapshotFuzzEnv::CheckCreateSnapshotManager(
+        const SnapshotFuzzData& data) {
+    auto partition_opener = std::make_unique<TestPartitionOpener>(super());
+    CheckWriteSuperMetadata(data, *partition_opener);
+    auto metadata_dir = fake_root_->tmp_path() + "/snapshot_metadata";
+    PCHECK(Mkdir(metadata_dir));
+
+    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());
+    snapshot->has_local_image_manager_ = data.manager_data().is_local_image_manager();
+
+    return snapshot;
+}
+
+const std::string& SnapshotFuzzEnv::super() const {
+    return fake_super_;
+}
+
+void SnapshotFuzzEnv::CheckWriteSuperMetadata(const SnapshotFuzzData& data,
+                                              const IPartitionOpener& opener) {
+    if (!data.is_super_metadata_valid()) {
+        // Leave it zero.
+        return;
+    }
+
+    BlockDeviceInfo super_device("super", SUPER_IMAGE_SIZE, 0, 0, 4096);
+    std::vector<BlockDeviceInfo> devices = {super_device};
+    auto builder = MetadataBuilder::New(devices, "super", 65536, 2);
+    CHECK(builder != nullptr);
+
+    // Attempt to create a super partition metadata using proto. All errors are ignored.
+    for (const auto& group_proto : data.super_data().dynamic_partition_metadata().groups()) {
+        (void)builder->AddGroup(group_proto.name(), group_proto.size());
+        for (const auto& partition_name : group_proto.partition_names()) {
+            (void)builder->AddPartition(partition_name, group_proto.name(),
+                                        LP_PARTITION_ATTR_READONLY);
+        }
+    }
+
+    for (const auto& partition_proto : data.super_data().partitions()) {
+        auto p = builder->FindPartition(partition_proto.partition_name());
+        if (p == nullptr) continue;
+        (void)builder->ResizePartition(p, partition_proto.new_partition_info().size());
+    }
+
+    auto metadata = builder->Export();
+    // metadata may be nullptr if it is not valid (e.g. partition name too long).
+    // In this case, just use empty super partition data.
+    if (metadata == nullptr) {
+        builder = MetadataBuilder::New(devices, "super", 65536, 2);
+        CHECK(builder != nullptr);
+        metadata = builder->Export();
+        CHECK(metadata != nullptr);
+    }
+    CHECK(FlashPartitionTable(opener, super(), *metadata.get()));
+}
+
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
new file mode 100644
index 0000000..5533def
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -0,0 +1,119 @@
+// 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 <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android/snapshot/snapshot_fuzz.pb.h>
+#include <libdm/loop_control.h>
+#include <libfiemap/image_manager.h>
+#include <liblp/liblp.h>
+#include <libsnapshot/auto_device.h>
+#include <libsnapshot/test_helpers.h>
+
+// libsnapshot-specific code for fuzzing. Defines fake classes that are depended
+// by SnapshotManager.
+
+#include "android/snapshot/snapshot_fuzz.pb.h"
+
+namespace android::snapshot {
+
+class AutoMemBasedDir;
+
+class DummyAutoDevice : public AutoDevice {
+  public:
+    DummyAutoDevice(bool mounted) : AutoDevice(mounted ? "dummy" : "") {}
+};
+
+// Prepare test environment. This has a heavy overhead and should be done once.
+class SnapshotFuzzEnv {
+  public:
+    // Check if test should run at all.
+    static bool ShouldSkipTest();
+
+    // Initialize the environment.
+    SnapshotFuzzEnv();
+    ~SnapshotFuzzEnv();
+
+    // Soft reset part of the environment before running the next test.
+    // Abort if fails.
+    void CheckSoftReset();
+
+    // 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);
+
+    // Return path to super partition.
+    const std::string& super() const;
+
+  private:
+    std::unique_ptr<AutoMemBasedDir> fake_root_;
+    std::unique_ptr<android::dm::LoopControl> loop_control_;
+    std::unique_ptr<AutoDevice> mapped_super_;
+    std::string fake_super_;
+
+    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,
+                                                     android::dm::LoopControl* control,
+                                                     std::string* fake_super);
+
+    void CheckWriteSuperMetadata(const SnapshotFuzzData& proto,
+                                 const android::fs_mgr::IPartitionOpener& opener);
+};
+
+class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo {
+  public:
+    // Client is responsible for maintaining the lifetime of |data|.
+    SnapshotFuzzDeviceInfo(const FuzzDeviceInfoData& data,
+                           std::unique_ptr<TestPartitionOpener>&& partition_opener,
+                           const std::string& metadata_dir)
+        : data_(&data),
+          partition_opener_(std::move(partition_opener)),
+          metadata_dir_(metadata_dir) {}
+
+    // Following APIs are mocked.
+    std::string GetGsidDir() const override { return "fuzz_ota"; }
+    std::string GetMetadataDir() const override { return metadata_dir_; }
+    std::string GetSuperDevice(uint32_t) const override {
+        // TestPartitionOpener can recognize this.
+        return "super";
+    }
+    const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override {
+        return *partition_opener_;
+    }
+
+    // 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";
+    }
+    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();
+    }
+    bool SetSlotAsUnbootable(unsigned int) override {
+        return data_->allow_set_slot_as_unbootable();
+    }
+    bool IsRecovery() const override { return data_->is_recovery(); }
+
+  private:
+    const FuzzDeviceInfoData* data_;
+    std::unique_ptr<TestPartitionOpener> partition_opener_;
+    std::string metadata_dir_;
+};
+
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 2747b03..2aaa78c 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -89,6 +89,11 @@
     return false;
 }
 
+bool SnapshotManagerStub::FinishMergeInRecovery() {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return false;
+}
+
 CreateResult SnapshotManagerStub::RecoveryCreateSnapshotDevices() {
     LOG(ERROR) << __FUNCTION__ << " should never be called.";
     return CreateResult::ERROR;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 53a37be..2bd0135 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -1459,6 +1459,52 @@
     ASSERT_EQ(new_sm->GetUpdateState(), UpdateState::None);
 }
 
+// Test that a merge does not clear the snapshot state in fastboot.
+TEST_F(SnapshotUpdateTest, MergeInFastboot) {
+    // Execute the first update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(MapUpdateSnapshots());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // After reboot, init does first stage mount.
+    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+    init = nullptr;
+
+    // Initiate the merge and then immediately stop it to simulate a reboot.
+    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+    ASSERT_TRUE(new_sm->InitiateMerge());
+    ASSERT_TRUE(UnmapAll());
+
+    // Simulate a reboot into recovery.
+    auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+    test_device->set_recovery(true);
+    new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+
+    ASSERT_TRUE(new_sm->FinishMergeInRecovery());
+
+    auto mount = new_sm->EnsureMetadataMounted();
+    ASSERT_TRUE(mount && mount->HasDevice());
+    ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
+
+    // Finish the merge in a normal boot.
+    test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+    init = SnapshotManager::NewForFirstStageMount(test_device.release());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+    init = nullptr;
+
+    test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+    new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+    ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
+    ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::None);
+}
+
 // Test that after an OTA, before a merge, we can wipe data in recovery.
 TEST_F(SnapshotUpdateTest, DataWipeRollbackInRecovery) {
     // Execute the first update.
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index b036606..f82a602 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -212,8 +212,8 @@
         return AssertionFailure() << strerror(errno);
     }
     bsize_ = buf.f_bsize;
-    free_space_ = buf.f_bsize * buf.f_bfree;
-    available_space_ = buf.f_bsize * buf.f_bavail;
+    free_space_ = bsize_ * buf.f_bfree;
+    available_space_ = bsize_ * buf.f_bavail;
     return AssertionSuccess();
 }
 
diff --git a/fs_mgr/libsnapshot/update_engine/update_metadata.proto b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
new file mode 100644
index 0000000..be5e1fe
--- /dev/null
+++ b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
@@ -0,0 +1,75 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// A subset of system/update_engine/update_metadata.proto. A separate file is
+// used here because:
+// - The original file is optimized for LITE_RUNTIME, but fuzzing needs
+// reflection.
+// - The definition here has less fields. libsnapshot only uses fields declared
+// here, and all fields declared here are fuzzed by libsnapshot_fuzzer. If
+// libsnapshot uses more fields in system/update_engine/update_metadata.proto
+// in the future, they must be added here too, otherwise it will fail to
+// compile.
+//
+// It is okay that this file is older than
+// system/update_engine/update_metadata.proto as long as the messages defined
+// here can also be parsed by protobuf defined there. However, it is not
+// okay to add fields here without adding them to
+// system/update_engine/update_metadata.proto. Doing so will cause a compiler
+// error when libsnapshot code starts to use these dangling fields.
+
+syntax = "proto2";
+
+package chromeos_update_engine;
+
+message Extent {
+    optional uint64 start_block = 1;
+    optional uint64 num_blocks = 2;
+}
+
+message PartitionInfo {
+    optional uint64 size = 1;
+}
+
+message InstallOperation {
+    enum Type { SOURCE_COPY = 4; }
+    required Type type = 1;
+    repeated Extent src_extents = 4;
+    repeated Extent dst_extents = 6;
+}
+
+message PartitionUpdate {
+    required string partition_name = 1;
+    optional PartitionInfo new_partition_info = 7;
+    repeated InstallOperation operations = 8;
+    optional Extent hash_tree_extent = 11;
+    optional Extent fec_extent = 15;
+}
+
+message DynamicPartitionGroup {
+    required string name = 1;
+    optional uint64 size = 2;
+    repeated string partition_names = 3;
+}
+
+message DynamicPartitionMetadata {
+    repeated DynamicPartitionGroup groups = 1;
+}
+
+message DeltaArchiveManifest {
+    repeated PartitionUpdate partitions = 13;
+    optional DynamicPartitionMetadata dynamic_partition_metadata = 15;
+}
diff --git a/init/README.md b/init/README.md
index 13f1bac..726c0cc 100644
--- a/init/README.md
+++ b/init/README.md
@@ -322,6 +322,10 @@
   This is mutually exclusive with the console option, which additionally connects stdin to the
   given console.
 
+`task_profiles <profile> [ <profile>\* ]`
+> Set task profiles for the process when it forks. This is designed to replace the use of
+  writepid option for moving a process into a cgroup.
+
 `timeout_period <seconds>`
 > Provide a timeout after which point the service will be killed. The oneshot keyword is respected
   here, so oneshot services do not automatically restart, however all other services will.
@@ -356,6 +360,8 @@
   cgroup/cpuset usage. If no files under /dev/cpuset/ are specified, but the
   system property 'ro.cpuset.default' is set to a non-empty cpuset name (e.g.
   '/foreground'), then the pid is written to file /dev/cpuset/_cpuset\_name_/tasks.
+  The use of this option for moving a process into a cgroup is obsolete. Please
+  use task_profiles option instead.
 
 
 Triggers
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
index cae53f4..cfa0d99 100644
--- a/init/first_stage_console.cpp
+++ b/init/first_stage_console.cpp
@@ -16,6 +16,7 @@
 
 #include "first_stage_console.h"
 
+#include <stdio.h>
 #include <sys/stat.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
@@ -87,8 +88,18 @@
     _exit(127);
 }
 
-bool FirstStageConsole(const std::string& cmdline) {
-    return cmdline.find("androidboot.first_stage_console=1") != std::string::npos;
+int FirstStageConsole(const std::string& cmdline) {
+    auto pos = cmdline.find("androidboot.first_stage_console=");
+    if (pos != std::string::npos) {
+        int val = 0;
+        if (sscanf(cmdline.c_str() + pos, "androidboot.first_stage_console=%d", &val) != 1) {
+            return FirstStageConsoleParam::DISABLED;
+        }
+        if (val <= FirstStageConsoleParam::MAX_PARAM_VALUE && val >= 0) {
+            return val;
+        }
+    }
+    return FirstStageConsoleParam::DISABLED;
 }
 
 }  // namespace init
diff --git a/init/first_stage_console.h b/init/first_stage_console.h
index 7485339..8f36a7c 100644
--- a/init/first_stage_console.h
+++ b/init/first_stage_console.h
@@ -21,8 +21,15 @@
 namespace android {
 namespace init {
 
+enum FirstStageConsoleParam {
+    DISABLED = 0,
+    CONSOLE_ON_FAILURE = 1,
+    IGNORE_FAILURE = 2,
+    MAX_PARAM_VALUE = IGNORE_FAILURE,
+};
+
 void StartConsole();
-bool FirstStageConsole(const std::string& cmdline);
+int FirstStageConsole(const std::string& cmdline);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 5eca644..1a608f6 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -200,16 +200,16 @@
     }
 
     Modprobe m({"/lib/modules"}, module_load_file);
-    auto want_console = ALLOW_FIRST_STAGE_CONSOLE && FirstStageConsole(cmdline);
+    auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline) : 0;
     if (!m.LoadListedModules(!want_console)) {
-        if (want_console) {
+        if (want_console != FirstStageConsoleParam::DISABLED) {
             LOG(ERROR) << "Failed to load kernel modules, starting console";
         } else {
             LOG(FATAL) << "Failed to load kernel modules";
         }
     }
 
-    if (want_console) {
+    if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
         StartConsole();
     }
 
diff --git a/init/init.cpp b/init/init.cpp
index a9d6301..3f8f628 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -76,6 +76,7 @@
 #include "service.h"
 #include "service_parser.h"
 #include "sigchld_handler.h"
+#include "subcontext.h"
 #include "system/core/init/property_service.pb.h"
 #include "util.h"
 
@@ -100,8 +101,6 @@
 static int signal_fd = -1;
 static int property_fd = -1;
 
-static std::unique_ptr<Subcontext> subcontext;
-
 struct PendingControlMessage {
     std::string message;
     std::string name;
@@ -216,16 +215,6 @@
     prop_waiter_state.ResetWaitForProp();
 }
 
-static void UnwindMainThreadStack() {
-    std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 1));
-    if (!backtrace->Unwind(0)) {
-        LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack.";
-    }
-    for (size_t i = 0; i < backtrace->NumFrames(); i++) {
-        LOG(ERROR) << backtrace->FormatFrameData(i);
-    }
-}
-
 static class ShutdownState {
   public:
     void TriggerShutdown(const std::string& command) {
@@ -243,13 +232,13 @@
     std::optional<std::string> CheckShutdown() {
         auto lock = std::lock_guard{shutdown_command_lock_};
         if (do_shutdown_ && !IsShuttingDown()) {
-            do_shutdown_ = false;
             return shutdown_command_;
         }
         return {};
     }
 
     bool do_shutdown() const { return do_shutdown_; }
+    void set_do_shutdown(bool value) { do_shutdown_ = value; }
 
   private:
     std::mutex shutdown_command_lock_;
@@ -257,16 +246,28 @@
     bool do_shutdown_ = false;
 } shutdown_state;
 
+static void UnwindMainThreadStack() {
+    std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 1));
+    if (!backtrace->Unwind(0)) {
+        LOG(ERROR) << __FUNCTION__ << "sys.powerctl: Failed to unwind callstack.";
+    }
+    for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+        LOG(ERROR) << "sys.powerctl: " << backtrace->FormatFrameData(i);
+    }
+}
+
 void DebugRebootLogging() {
-    LOG(INFO) << "do_shutdown: " << shutdown_state.do_shutdown()
+    LOG(INFO) << "sys.powerctl: do_shutdown: " << shutdown_state.do_shutdown()
               << " IsShuttingDown: " << IsShuttingDown();
     if (shutdown_state.do_shutdown()) {
         LOG(ERROR) << "sys.powerctl set while a previous shutdown command has not been handled";
         UnwindMainThreadStack();
+        DumpShutdownDebugInformation();
     }
     if (IsShuttingDown()) {
         LOG(ERROR) << "sys.powerctl set while init is already shutting down";
         UnwindMainThreadStack();
+        DumpShutdownDebugInformation();
     }
 }
 
@@ -279,9 +280,8 @@
     Parser parser;
 
     parser.AddSectionParser("service", std::make_unique<ServiceParser>(
-                                               &service_list, subcontext.get(), std::nullopt));
-    parser.AddSectionParser("on",
-                            std::make_unique<ActionParser>(&action_manager, subcontext.get()));
+                                               &service_list, GetSubcontext(), std::nullopt));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
     return parser;
@@ -291,9 +291,9 @@
 Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex) {
     Parser parser;
 
-    parser.AddSectionParser("service",
-                            std::make_unique<ServiceParser>(&service_list, subcontext.get(),
-                                                            std::nullopt, from_apex));
+    parser.AddSectionParser(
+            "service", std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt,
+                                                       from_apex));
     return parser;
 }
 
@@ -721,7 +721,7 @@
     trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
 
     SetStdioToDevNull(argv);
-    InitKernelLogging(argv);
+    InitSecondStageLogging(argv);
     LOG(INFO) << "init second stage started!";
 
     // Init should not crash because of a dependence on any other process, therefore we ignore
@@ -809,7 +809,7 @@
         PLOG(FATAL) << "SetupMountNamespaces failed";
     }
 
-    subcontext = InitializeSubcontext();
+    InitializeSubcontext();
 
     ActionManager& am = ActionManager::GetInstance();
     ServiceList& sm = ServiceList::GetInstance();
@@ -874,6 +874,7 @@
             LOG(INFO) << "Got shutdown_command '" << *shutdown_command
                       << "' Calling HandlePowerctlMessage()";
             HandlePowerctlMessage(*shutdown_command);
+            shutdown_state.set_do_shutdown(false);
         }
 
         if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
diff --git a/init/init_test.cpp b/init/init_test.cpp
index caf3e03..07b4724 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -239,6 +239,28 @@
     EXPECT_EQ(6, num_executed);
 }
 
+TEST(init, RejectsCriticalAndOneshotService) {
+    std::string init_script =
+            R"init(
+service A something
+  class first
+  critical
+  oneshot
+)init";
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
+
+    ServiceList service_list;
+    Parser parser;
+    parser.AddSectionParser("service",
+                            std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+
+    ASSERT_TRUE(parser.ParseConfig(tf.path));
+    ASSERT_EQ(1u, parser.parse_error_count());
+}
+
 }  // namespace init
 }  // namespace android
 
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 72f0450..ffd58a3 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -66,8 +66,6 @@
 #include "sigchld_handler.h"
 #include "util.h"
 
-#define PROC_SYSRQ "/proc/sysrq-trigger"
-
 using namespace std::literals;
 
 using android::base::boot_clock;
@@ -758,20 +756,24 @@
 
 static Result<void> DoUserspaceReboot() {
     LOG(INFO) << "Userspace reboot initiated";
-    auto guard = android::base::make_scope_guard([] {
+    // An ugly way to pass a more precise reason on why fallback to hard reboot was triggered.
+    std::string sub_reason = "";
+    auto guard = android::base::make_scope_guard([&sub_reason] {
         // Leave shutdown so that we can handle a full reboot.
         LeaveShutdown();
-        trigger_shutdown("reboot,userspace_failed,shutdown_aborted");
+        trigger_shutdown("reboot,userspace_failed,shutdown_aborted," + sub_reason);
     });
     // Triggering userspace-reboot-requested will result in a bunch of setprop
     // actions. We should make sure, that all of them are propagated before
     // proceeding with userspace reboot. Synchronously setting sys.init.userspace_reboot.in_progress
     // property is not perfect, but it should do the trick.
     if (!android::sysprop::InitProperties::userspace_reboot_in_progress(true)) {
+        sub_reason = "setprop";
         return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
     }
     EnterShutdown();
     if (!SetProperty("sys.powerctl", "")) {
+        sub_reason = "resetprop";
         return Error() << "Failed to reset sys.powerctl property";
     }
     std::vector<Service*> stop_first;
@@ -800,18 +802,22 @@
     StopServicesAndLogViolations(stop_first, sigterm_timeout, true /* SIGTERM */);
     if (int r = StopServicesAndLogViolations(stop_first, sigkill_timeout, false /* SIGKILL */);
         r > 0) {
+        sub_reason = "sigkill";
         // TODO(b/135984674): store information about offending services for debugging.
         return Error() << r << " post-data services are still running";
     }
     if (auto result = KillZramBackingDevice(); !result.ok()) {
+        sub_reason = "zram";
         return result;
     }
     if (auto result = CallVdc("volume", "reset"); !result.ok()) {
+        sub_reason = "vold_reset";
         return result;
     }
     if (int r = StopServicesAndLogViolations(GetDebuggingServices(true /* only_post_data */),
                                              sigkill_timeout, false /* SIGKILL */);
         r > 0) {
+        sub_reason = "sigkill_debug";
         // TODO(b/135984674): store information about offending services for debugging.
         return Error() << r << " debugging services are still running";
     }
@@ -822,9 +828,11 @@
         LOG(INFO) << "sync() took " << sync_timer;
     }
     if (auto result = UnmountAllApexes(); !result.ok()) {
+        sub_reason = "apex";
         return result;
     }
     if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
+        sub_reason = "ns_switch";
         return Error() << "Failed to switch to bootstrap namespace";
     }
     // Remove services that were defined in an APEX.
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index 485188b..76460a5 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -29,6 +29,7 @@
 #include <cutils/android_reboot.h>
 
 #include "capabilities.h"
+#include "reboot_utils.h"
 
 namespace android {
 namespace init {
@@ -138,6 +139,9 @@
         LOG(ERROR) << backtrace->FormatFrameData(i);
     }
     if (init_fatal_panic) {
+        LOG(ERROR) << __FUNCTION__ << ": Trigger crash";
+        android::base::WriteStringToFile("c", PROC_SYSRQ);
+        LOG(ERROR) << __FUNCTION__ << ": Sys-Rq failed to crash the system; fallback to exit().";
         _exit(signal_number);
     }
     RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
diff --git a/init/reboot_utils.h b/init/reboot_utils.h
index 878ad96..05bb9ae 100644
--- a/init/reboot_utils.h
+++ b/init/reboot_utils.h
@@ -18,6 +18,8 @@
 
 #include <string>
 
+#define PROC_SYSRQ "/proc/sysrq-trigger"
+
 namespace android {
 namespace init {
 
diff --git a/init/service.cpp b/init/service.cpp
index 20400a0..165b848 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -513,6 +513,10 @@
             LOG(ERROR) << "failed to write pid to files: " << result.error();
         }
 
+        if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
+            LOG(ERROR) << "failed to set task profiles";
+        }
+
         // As requested, set our gid, supplemental gids, uid, context, and
         // priority. Aborts on failure.
         SetProcessAttributesAndCaps();
diff --git a/init/service.h b/init/service.h
index 9f1d697..34ed5ef 100644
--- a/init/service.h
+++ b/init/service.h
@@ -170,6 +170,8 @@
 
     std::vector<std::string> writepid_files_;
 
+    std::vector<std::string> task_profiles_;
+
     std::set<std::string> interfaces_;  // e.g. some.package.foo@1.0::IBaz/instance-name
 
     // keycodes for triggering this service via /dev/input/input*
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 560f693..bdac077 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -360,6 +360,12 @@
     return Error() << "Invalid shutdown option";
 }
 
+Result<void> ServiceParser::ParseTaskProfiles(std::vector<std::string>&& args) {
+    args.erase(args.begin());
+    service_->task_profiles_ = std::move(args);
+    return {};
+}
+
 Result<void> ServiceParser::ParseTimeoutPeriod(std::vector<std::string>&& args) {
     int period;
     if (!ParseInt(args[1], &period, 1)) {
@@ -529,6 +535,7 @@
         {"sigstop",                 {0,     0,    &ServiceParser::ParseSigstop}},
         {"socket",                  {3,     6,    &ServiceParser::ParseSocket}},
         {"stdio_to_kmsg",           {0,     0,    &ServiceParser::ParseStdioToKmsg}},
+        {"task_profiles",           {1,     kMax, &ServiceParser::ParseTaskProfiles}},
         {"timeout_period",          {1,     1,    &ServiceParser::ParseTimeoutPeriod}},
         {"updatable",               {0,     0,    &ServiceParser::ParseUpdatable}},
         {"user",                    {1,     1,    &ServiceParser::ParseUser}},
@@ -598,6 +605,13 @@
         }
     }
 
+    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
+        if ((service_->flags() & SVC_CRITICAL) != 0 && (service_->flags() & SVC_ONESHOT) != 0) {
+            return Error() << "service '" << service_->name()
+                           << "' can't be both critical and oneshot";
+        }
+    }
+
     Service* old_service = service_list_->FindService(service_->name());
     if (old_service) {
         if (!service_->is_override()) {
diff --git a/init/service_parser.h b/init/service_parser.h
index 7bb0cc0..0fd2da5 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -78,6 +78,7 @@
     Result<void> ParseSigstop(std::vector<std::string>&& args);
     Result<void> ParseSocket(std::vector<std::string>&& args);
     Result<void> ParseStdioToKmsg(std::vector<std::string>&& args);
+    Result<void> ParseTaskProfiles(std::vector<std::string>&& args);
     Result<void> ParseTimeoutPeriod(std::vector<std::string>&& args);
     Result<void> ParseFile(std::vector<std::string>&& args);
     Result<void> ParseUser(std::vector<std::string>&& args);
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 5263c14..f3dd538 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -52,6 +52,8 @@
 namespace {
 
 std::string shutdown_command;
+static bool subcontext_terminated_by_shutdown;
+static std::unique_ptr<Subcontext> subcontext;
 
 class SubcontextProcess {
   public:
@@ -323,34 +325,30 @@
     return expanded_args;
 }
 
-static std::vector<Subcontext> subcontexts;
-static bool shutting_down;
-
-std::unique_ptr<Subcontext> InitializeSubcontext() {
+void InitializeSubcontext() {
     if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
-        return std::make_unique<Subcontext>(std::vector<std::string>{"/vendor", "/odm"},
-                                            kVendorContext);
+        subcontext.reset(
+                new Subcontext(std::vector<std::string>{"/vendor", "/odm"}, kVendorContext));
     }
-    return nullptr;
+}
+
+Subcontext* GetSubcontext() {
+    return subcontext.get();
 }
 
 bool SubcontextChildReap(pid_t pid) {
-    for (auto& subcontext : subcontexts) {
-        if (subcontext.pid() == pid) {
-            if (!shutting_down) {
-                subcontext.Restart();
-            }
-            return true;
+    if (subcontext->pid() == pid) {
+        if (!subcontext_terminated_by_shutdown) {
+            subcontext->Restart();
         }
+        return true;
     }
     return false;
 }
 
 void SubcontextTerminate() {
-    shutting_down = true;
-    for (auto& subcontext : subcontexts) {
-        kill(subcontext.pid(), SIGTERM);
-    }
+    subcontext_terminated_by_shutdown = true;
+    kill(subcontext->pid(), SIGTERM);
 }
 
 }  // namespace init
diff --git a/init/subcontext.h b/init/subcontext.h
index 5e1d8a8..788d3be 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -60,7 +60,8 @@
 };
 
 int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map);
-std::unique_ptr<Subcontext> InitializeSubcontext();
+void InitializeSubcontext();
+Subcontext* GetSubcontext();
 bool SubcontextChildReap(pid_t pid);
 void SubcontextTerminate();
 
diff --git a/init/util.cpp b/init/util.cpp
index 24f94ec..f9be055 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -30,7 +30,9 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <mutex>
 #include <thread>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -660,5 +662,50 @@
     return access("/system/bin/recovery", F_OK) == 0;
 }
 
+// TODO(b/155203339): remove this
+// Devices in the lab seem to be stuck during shutdown, but the logs don't capture the last actions
+// before shutdown started, so we record those lines, ignoring requests to shutdown, and replay them
+// if we identify that the device is stuck.
+constexpr size_t kRecordedLogsSize = 30;
+std::string recorded_logs[kRecordedLogsSize];
+size_t recorded_log_position = 0;
+std::mutex recorded_logs_lock;
+
+void InitSecondStageLogging(char** argv) {
+    SetFatalRebootTarget();
+    auto second_stage_logger = [](android::base::LogId log_id, android::base::LogSeverity severity,
+                                  const char* tag, const char* file, unsigned int line,
+                                  const char* message) {
+        // We only store logs for init, not its children, and only if they're not related to
+        // sys.powerctl.
+        if (getpid() == 1 && strstr(message, "sys.powerctl") == nullptr) {
+            auto lock = std::lock_guard{recorded_logs_lock};
+            recorded_logs[recorded_log_position++] = message;
+            if (recorded_log_position == kRecordedLogsSize) {
+                recorded_log_position = 0;
+            }
+        }
+        android::base::KernelLogger(log_id, severity, tag, file, line, message);
+    };
+    android::base::InitLogging(argv, second_stage_logger, InitAborter);
+}
+
+void DumpShutdownDebugInformation() {
+    auto lock = std::lock_guard{recorded_logs_lock};
+    android::base::KernelLogger(
+            android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+            "===================== Dumping previous init lines =====================");
+    for (size_t i = recorded_log_position; i < kRecordedLogsSize; ++i) {
+        android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+                                    recorded_logs[i].c_str());
+    }
+    for (size_t i = 0; i < recorded_log_position; ++i) {
+        android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+                                    recorded_logs[i].c_str());
+    }
+    android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+                                "===================== End of dump =====================");
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/util.h b/init/util.h
index ad322d9..8167b02 100644
--- a/init/util.h
+++ b/init/util.h
@@ -78,6 +78,8 @@
 
 void SetStdioToDevNull(char** argv);
 void InitKernelLogging(char** argv);
+void InitSecondStageLogging(char** argv);
+void DumpShutdownDebugInformation();
 bool IsRecoveryMode();
 }  // namespace init
 }  // namespace android
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 175b2b7..d7c83a2 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -33,6 +33,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "29",
     native_bridge_supported: true,
     export_include_dirs: ["include"],
     target: {
@@ -59,6 +60,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "29",
 
     export_include_dirs: ["include"],
 
@@ -142,6 +144,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "29",
     native_bridge_supported: true,
     srcs: [
         "config_utils.cpp",
diff --git a/libcutils/include/cutils/ashmem.h b/libcutils/include/cutils/ashmem.h
index d80caa6..1913c1e 100644
--- a/libcutils/include/cutils/ashmem.h
+++ b/libcutils/include/cutils/ashmem.h
@@ -1,14 +1,20 @@
-/* cutils/ashmem.h
- **
- ** Copyright 2008 The Android Open Source Project
- **
- ** This file is dual licensed.  It may be redistributed and/or modified
- ** under the terms of the Apache 2.0 License OR version 2 of the GNU
- ** General Public License.
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
-#ifndef _CUTILS_ASHMEM_H
-#define _CUTILS_ASHMEM_H
+#pragma once
 
 #include <stddef.h>
 
@@ -30,5 +36,3 @@
 #ifdef __cplusplus
 }
 #endif
-
-#endif	/* _CUTILS_ASHMEM_H */
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index ce0c3c8..33ae13d 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -26,4 +26,5 @@
     export_include_dirs: ["include"],
     shared_libs: ["android.hardware.graphics.allocator@2.0"],
     header_libs: ["libhardware_headers"],
+    min_sdk_version: "29",
 }
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 0b98e1a..6051ac7 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -42,6 +42,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "29",
     native_bridge_supported: true,
     export_include_dirs: ["include"],
     system_shared_libs: [],
diff --git a/liblog/README.md b/liblog/README.md
index f64f376..74a2cd7 100644
--- a/liblog/README.md
+++ b/liblog/README.md
@@ -60,8 +60,6 @@
     LOG_EVENT_INT(tag, value)
     LOG_EVENT_LONG(tag, value)
 
-    clockid_t android_log_clockid()
-
     log_id_t android_logger_get_id(struct logger *logger)
     int android_logger_clear(struct logger *logger)
     int android_logger_get_log_size(struct logger *logger)
@@ -119,7 +117,7 @@
 multiple logs can be opened with `android_logger_list_alloc()`, calling in turn the
 `android_logger_open()` for each log id.  Each entry can be retrieved with
 `android_logger_list_read()`.  The log(s) can be closed with `android_logger_list_free()`.
-`ANDROID_LOG_NONBLOCK` mode will report when the log reading is done with an `EAGAIN` error return 
+`ANDROID_LOG_NONBLOCK` mode will report when the log reading is done with an `EAGAIN` error return
 code, otherwise the `android_logger_list_read()` call will block for new entries.
 
 The `ANDROID_LOG_WRAP` mode flag to the `android_logger_list_alloc_time()` signals logd to quiesce
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index 512c7cd..8a0ebf2 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -185,14 +185,26 @@
  * and sending log messages to user defined loggers specified in __android_log_set_logger().
  */
 struct __android_log_message {
-  size_t
-      struct_size;   /** Must be set to sizeof(__android_log_message) and is used for versioning. */
-  int32_t buffer_id; /** {@link log_id_t} values. */
-  int32_t priority;  /** {@link android_LogPriority} values. */
-  const char* tag;   /** The tag for the log message. */
-  const char* file;  /** Optional file name, may be set to nullptr. */
-  uint32_t line;     /** Optional line number, ignore if file is nullptr. */
-  const char* message; /** The log message itself. */
+  /** Must be set to sizeof(__android_log_message) and is used for versioning. */
+  size_t struct_size;
+
+  /** {@link log_id_t} values. */
+  int32_t buffer_id;
+
+  /** {@link android_LogPriority} values. */
+  int32_t priority;
+
+  /** The tag for the log message. */
+  const char* tag;
+
+  /** Optional file name, may be set to nullptr. */
+  const char* file;
+
+  /** Optional line number, ignore if file is nullptr. */
+  uint32_t line;
+
+  /** The log message itself. */
+  const char* message;
 };
 
 /**
@@ -215,7 +227,7 @@
  * buffers, then pass the message to liblog via this function, and therefore we do not want to
  * duplicate the loggability check here.
  *
- * @param log_message the log message itself, see {@link __android_log_message}.
+ * @param log_message the log message itself, see __android_log_message.
  *
  * Available since API level 30.
  */
@@ -237,7 +249,7 @@
  * Writes the log message to logd.  This is an __android_logger_function and can be provided to
  * __android_log_set_logger().  It is the default logger when running liblog on a device.
  *
- * @param log_message the log message to write, see {@link __android_log_message}.
+ * @param log_message the log message to write, see __android_log_message.
  *
  * Available since API level 30.
  */
@@ -247,7 +259,7 @@
  * Writes the log message to stderr.  This is an __android_logger_function and can be provided to
  * __android_log_set_logger().  It is the default logger when running liblog on host.
  *
- * @param log_message the log message to write, see {@link __android_log_message}.
+ * @param log_message the log message to write, see __android_log_message.
  *
  * Available since API level 30.
  */
@@ -259,7 +271,7 @@
  * user defined aborter function is highly recommended to abort and be noreturn, but is not strictly
  * required to.
  *
- * @param aborter the new aborter function, see {@link __android_aborter_function}.
+ * @param aborter the new aborter function, see __android_aborter_function.
  *
  * Available since API level 30.
  */
@@ -297,7 +309,26 @@
  * minimum priority needed to log.  If only one is set, then that value is used to determine the
  * minimum priority needed.  If none are set, then default_priority is used.
  *
- * @param prio         the priority to test, takes {@link android_LogPriority} values.
+ * @param prio         the priority to test, takes android_LogPriority values.
+ * @param tag          the tag to test.
+ * @param default_prio the default priority to use if no properties or minimum priority are set.
+ * @return an integer where 1 indicates that the message is loggable and 0 indicates that it is not.
+ *
+ * Available since API level 30.
+ */
+int __android_log_is_loggable(int prio, const char* tag, int default_prio) __INTRODUCED_IN(30);
+
+/**
+ * Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
+ * __android_log_set_minimum_priority() to determine if a log message with a given prio and tag will
+ * be printed.  A non-zero result indicates yes, zero indicates false.
+ *
+ * If both a priority for a tag and a minimum priority are set by
+ * __android_log_set_minimum_priority(), then the lowest of the two values are to determine the
+ * minimum priority needed to log.  If only one is set, then that value is used to determine the
+ * minimum priority needed.  If none are set, then default_priority is used.
+ *
+ * @param prio         the priority to test, takes android_LogPriority values.
  * @param tag          the tag to test.
  * @param len          the length of the tag.
  * @param default_prio the default priority to use if no properties or minimum priority are set.
@@ -305,15 +336,14 @@
  *
  * Available since API level 30.
  */
-int __android_log_is_loggable(int prio, const char* tag, int default_prio) __INTRODUCED_IN(30);
 int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio)
     __INTRODUCED_IN(30);
 
 /**
  * Sets the minimum priority that will be logged for this process.
  *
- * @param priority the new minimum priority to set, takes @{link android_LogPriority} values.
- * @return the previous set minimum priority as @{link android_LogPriority} values, or
+ * @param priority the new minimum priority to set, takes android_LogPriority values.
+ * @return the previous set minimum priority as android_LogPriority values, or
  *         ANDROID_LOG_DEFAULT if none was set.
  *
  * Available since API level 30.
@@ -324,7 +354,7 @@
  * Gets the minimum priority that will be logged for this process.  If none has been set by a
  * previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
  *
- * @return the current minimum priority as @{link android_LogPriority} values, or
+ * @return the current minimum priority as android_LogPriority values, or
  *         ANDROID_LOG_DEFAULT if none is set.
  *
  * Available since API level 30.
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 19edb83..d7e9b7d 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -29,7 +29,6 @@
 #include <log/log_id.h>
 #include <log/log_main.h>
 #include <log/log_radio.h>
-#include <log/log_read.h>
 #include <log/log_safetynet.h>
 #include <log/log_system.h>
 #include <log/log_time.h>
@@ -65,6 +64,13 @@
 #endif
 
 /*
+ * The maximum size of the log entry payload that can be
+ * written to the logger. An attempt to write more than
+ * this amount will result in a truncated log entry.
+ */
+#define LOGGER_ENTRY_MAX_PAYLOAD 4068
+
+/*
  * Event logging.
  */
 
@@ -127,19 +133,16 @@
   (void)__android_log_bswrite(_tag, _value);
 #endif
 
-#ifdef __linux__
-
-clockid_t android_log_clockid(void);
-
-#endif /* __linux__ */
-
 /* --------------------------------------------------------------------- */
 
 /*
  * Release any logger resources (a new log write will immediately re-acquire)
  *
- * May be used to clean up File descriptors after a Fork, the resources are
- * all O_CLOEXEC so wil self clean on exec().
+ * This is specifically meant to be used by Zygote to close open file descriptors after fork()
+ * and before specialization.  O_CLOEXEC is used on file descriptors, so they will be closed upon
+ * exec() in normal use cases.
+ *
+ * Note that this is not safe to call from a multi-threaded program.
  */
 void __android_log_close(void);
 
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
index c8fafe7..8e4faeb 100644
--- a/liblog/include/log/log_id.h
+++ b/liblog/include/log/log_id.h
@@ -23,14 +23,6 @@
 #endif
 
 /*
- * Send a simple string to the log.
- */
-int __android_log_buf_write(int bufID, int prio, const char* tag,
-                            const char* text);
-int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
-    __attribute__((__format__(printf, 4, 5)));
-
-/*
  * log_id_t helpers
  */
 log_id_t android_name_to_log_id(const char* logName);
diff --git a/liblog/include/log/log_properties.h b/liblog/include/log/log_properties.h
index 3a8af6d..3497d63 100644
--- a/liblog/include/log/log_properties.h
+++ b/liblog/include/log/log_properties.h
@@ -1,11 +1,18 @@
 /*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** This file is dual licensed.  It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 #pragma once
 
diff --git a/liblog/include/log/log_radio.h b/liblog/include/log/log_radio.h
index 8b8a362..f5525c1 100644
--- a/liblog/include/log/log_radio.h
+++ b/liblog/include/log/log_radio.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include <android/log.h>
-#include <log/log_id.h>
 
 /*
  * Normally we strip the effects of ALOGV (VERBOSE messages),
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index e2bc297..24b88d2 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -19,7 +19,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <log/log_id.h>
+#include <android/log.h>
 #include <log/log_time.h>
 
 #ifdef __cplusplus
@@ -48,13 +48,6 @@
 };
 
 /*
- * The maximum size of the log entry payload that can be
- * written to the logger. An attempt to write more than
- * this amount will result in a truncated log entry.
- */
-#define LOGGER_ENTRY_MAX_PAYLOAD 4068
-
-/*
  * The maximum size of a log entry which can be read.
  * An attempt to read less than this amount may result
  * in read() returning EINVAL.
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
index d3e9b19..b2604b5 100644
--- a/liblog/include/log/log_safetynet.h
+++ b/liblog/include/log/log_safetynet.h
@@ -1,11 +1,18 @@
 /*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** This file is dual licensed.  It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 #pragma once
 
diff --git a/liblog/include/log/log_system.h b/liblog/include/log/log_system.h
index eaec741..6f40515 100644
--- a/liblog/include/log/log_system.h
+++ b/liblog/include/log/log_system.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include <android/log.h>
-#include <log/log_id.h>
 
 /*
  * Normally we strip the effects of ALOGV (VERBOSE messages),
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index 67376f4..a230749 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -32,58 +32,53 @@
 #include <time.h>
 #include <unistd.h>
 
-#include <shared_mutex>
-
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
 #include "logger.h"
-#include "rwlock.h"
 #include "uio.h"
 
-static int logd_socket;
-static RwLock logd_socket_lock;
+static atomic_int logd_socket;
 
-static void OpenSocketLocked() {
-  logd_socket = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
-  if (logd_socket <= 0) {
-    return;
-  }
-
+// Note that it is safe to call connect() multiple times on DGRAM Unix domain sockets, so this
+// function is used to reconnect to logd without requiring a new socket.
+static void LogdConnect() {
   sockaddr_un un = {};
   un.sun_family = AF_UNIX;
   strcpy(un.sun_path, "/dev/socket/logdw");
-
-  if (TEMP_FAILURE_RETRY(
-          connect(logd_socket, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un))) < 0) {
-    close(logd_socket);
-    logd_socket = 0;
-  }
+  TEMP_FAILURE_RETRY(connect(logd_socket, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un)));
 }
 
-static void OpenSocket() {
-  auto lock = std::unique_lock{logd_socket_lock};
-  if (logd_socket > 0) {
-    // Someone raced us and opened the socket already.
+// logd_socket should only be opened once.  If we see that logd_socket is uninitialized, we create a
+// new socket and attempt to exchange it into the atomic logd_socket.  If the compare/exchange was
+// successful, then that will be the socket used for the duration of the program, otherwise a
+// different thread has already opened and written the socket to the atomic, so close the new socket
+// and return.
+static void GetSocket() {
+  if (logd_socket != 0) {
     return;
   }
 
-  OpenSocketLocked();
-}
-
-static void ResetSocket(int old_socket) {
-  auto lock = std::unique_lock{logd_socket_lock};
-  if (old_socket != logd_socket) {
-    // Someone raced us and reset the socket already.
+  int new_socket =
+      TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+  if (new_socket <= 0) {
     return;
   }
-  close(logd_socket);
-  logd_socket = 0;
-  OpenSocketLocked();
+
+  int uninitialized_value = 0;
+  if (!logd_socket.compare_exchange_strong(uninitialized_value, new_socket)) {
+    close(new_socket);
+    return;
+  }
+
+  LogdConnect();
 }
 
+// This is the one exception to the above.  Zygote uses this to clean up open FD's after fork() and
+// before specialization.  It is single threaded at this point and therefore this function is
+// explicitly not thread safe.  It sets logd_socket to 0, so future logs will be safely initialized
+// whenever they happen.
 void LogdClose() {
-  auto lock = std::unique_lock{logd_socket_lock};
   if (logd_socket > 0) {
     close(logd_socket);
   }
@@ -99,12 +94,7 @@
   static atomic_int dropped;
   static atomic_int droppedSecurity;
 
-  auto lock = std::shared_lock{logd_socket_lock};
-  if (logd_socket <= 0) {
-    lock.unlock();
-    OpenSocket();
-    lock.lock();
-  }
+  GetSocket();
 
   if (logd_socket <= 0) {
     return -EBADF;
@@ -183,10 +173,7 @@
   // the connection, so we reset it and try again.
   ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
   if (ret < 0 && errno != EAGAIN) {
-    int old_socket = logd_socket;
-    lock.unlock();
-    ResetSocket(old_socket);
-    lock.lock();
+    LogdConnect();
 
     ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
   }
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index 3a75fa3..22c7eca 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -192,7 +192,7 @@
     return -EINVAL;
   }
 
-  clock_gettime(android_log_clockid(), &ts);
+  clock_gettime(CLOCK_REALTIME, &ts);
 
   if (log_id == LOG_ID_SECURITY) {
     if (vec[0].iov_len < 4) {
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index 5c69bf8..238431f 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -19,6 +19,8 @@
 #define HAVE_STRSEP
 #endif
 
+#include <log/logprint.h>
+
 #include <assert.h>
 #include <ctype.h>
 #include <errno.h>
@@ -37,7 +39,7 @@
 #include <cutils/list.h>
 
 #include <log/log.h>
-#include <log/logprint.h>
+#include <log/log_read.h>
 #include <private/android_logger.h>
 
 #define MS_PER_NSEC 1000000
@@ -214,11 +216,7 @@
   p_ret->year_output = false;
   p_ret->zone_output = false;
   p_ret->epoch_output = false;
-#ifdef __ANDROID__
-  p_ret->monotonic_output = android_log_clockid() == CLOCK_MONOTONIC;
-#else
   p_ret->monotonic_output = false;
-#endif
   p_ret->uid_output = false;
   p_ret->descriptive_output = false;
   descriptive_output = false;
@@ -1463,13 +1461,10 @@
   nsec = entry->tv_nsec;
 #if __ANDROID__
   if (p_format->monotonic_output) {
-    /* prevent convertMonotonic from being called if logd is monotonic */
-    if (android_log_clockid() != CLOCK_MONOTONIC) {
-      struct timespec time;
-      convertMonotonic(&time, entry);
-      now = time.tv_sec;
-      nsec = time.tv_nsec;
-    }
+    struct timespec time;
+    convertMonotonic(&time, entry);
+    now = time.tv_sec;
+    nsec = time.tv_nsec;
   }
 #endif
   if (now < 0) {
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 06e5e04..8e676bd 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -23,30 +23,36 @@
 #include <sys/types.h>
 #include <time.h>
 
-#include <shared_mutex>
-
 #include <log/log_properties.h>
 #include <private/android_logger.h>
 
 #include "logger.h"
-#include "rwlock.h"
 #include "uio.h"
 
-static int pmsg_fd;
-static RwLock pmsg_fd_lock;
+static atomic_int pmsg_fd;
 
-static void PmsgOpen() {
-  auto lock = std::unique_lock{pmsg_fd_lock};
-  if (pmsg_fd > 0) {
-    // Someone raced us and opened the socket already.
+// pmsg_fd should only beopened once.  If we see that pmsg_fd is uninitialized, we open "/dev/pmsg0"
+// then attempt to compare/exchange it into pmsg_fd.  If the compare/exchange was successful, then
+// that will be the fd used for the duration of the program, otherwise a different thread has
+// already opened and written the fd to the atomic, so close the new fd and return.
+static void GetPmsgFd() {
+  if (pmsg_fd != 0) {
     return;
   }
 
-  pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+  int new_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+  if (new_fd <= 0) {
+    return;
+  }
+
+  int uninitialized_value = 0;
+  if (!pmsg_fd.compare_exchange_strong(uninitialized_value, new_fd)) {
+    close(new_fd);
+    return;
+  }
 }
 
 void PmsgClose() {
-  auto lock = std::unique_lock{pmsg_fd_lock};
   if (pmsg_fd > 0) {
     close(pmsg_fd);
   }
@@ -77,13 +83,7 @@
     }
   }
 
-  auto lock = std::shared_lock{pmsg_fd_lock};
-
-  if (pmsg_fd <= 0) {
-    lock.unlock();
-    PmsgOpen();
-    lock.lock();
-  }
+  GetPmsgFd();
 
   if (pmsg_fd <= 0) {
     return -EBADF;
@@ -188,7 +188,7 @@
     return -EINVAL;
   }
 
-  clock_gettime(android_log_clockid(), &ts);
+  clock_gettime(CLOCK_REALTIME, &ts);
 
   cp = strdup(filename);
   if (!cp) {
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index 37670ec..f5e060c 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -365,29 +365,6 @@
   return c;
 }
 
-static unsigned char evaluate_persist_ro(const struct cache2_char* self) {
-  unsigned char c = self->cache_persist.c;
-
-  if (c) {
-    return c;
-  }
-
-  return self->cache_ro.c;
-}
-
-/*
- * Timestamp state generally remains constant, but can change at any time
- * to handle developer requirements.
- */
-clockid_t android_log_clockid() {
-  static struct cache2_char clockid = {PTHREAD_MUTEX_INITIALIZER, 0,
-                                       "persist.logd.timestamp",  {{NULL, 0xFFFFFFFF}, '\0'},
-                                       "ro.logd.timestamp",       {{NULL, 0xFFFFFFFF}, '\0'},
-                                       evaluate_persist_ro};
-
-  return (tolower(do_cache2_char(&clockid)) == 'm') ? CLOCK_MONOTONIC : CLOCK_REALTIME;
-}
-
 /*
  * Security state generally remains constant, but the DO must be able
  * to turn off logging should it become spammy after an attack is detected.
diff --git a/liblog/rwlock.h b/liblog/rwlock.h
deleted file mode 100644
index 00f1806..0000000
--- a/liblog/rwlock.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <pthread.h>
-
-// As of the end of Dec 2019, std::shared_mutex is *not* simply a pthread_rwlock, but rather a
-// combination of std::mutex and std::condition variable, which is obviously less efficient.  This
-// immitates what std::shared_mutex should be doing and is compatible with std::shared_lock and
-// std::unique_lock.
-
-class RwLock {
- public:
-  RwLock() {}
-  ~RwLock() {}
-
-  void lock() { pthread_rwlock_wrlock(&rwlock_); }
-  void unlock() { pthread_rwlock_unlock(&rwlock_); }
-
-  void lock_shared() { pthread_rwlock_rdlock(&rwlock_); }
-  void unlock_shared() { pthread_rwlock_unlock(&rwlock_); }
-
- private:
-  pthread_rwlock_t rwlock_ = PTHREAD_RWLOCK_INITIALIZER;
-};
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 385b079..2a6424b 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -63,8 +63,8 @@
         "log_system_test.cpp",
         "log_time_test.cpp",
         "log_wrap_test.cpp",
+        "logd_writer_test.cpp",
         "logprint_test.cpp",
-        "rwlock_test.cpp",
     ],
     shared_libs: [
         "libcutils",
@@ -72,6 +72,7 @@
     ],
     static_libs: ["liblog"],
     isolated: true,
+    require_root: true,
 }
 
 // Build tests for the device (with .so). Run with:
@@ -108,7 +109,6 @@
         "liblog_host_test.cpp",
         "liblog_default_tag.cpp",
         "liblog_global_state.cpp",
-        "rwlock_test.cpp",
     ],
     isolated: true,
 }
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 3a6ed90..a4e4def 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -31,6 +31,7 @@
 #include <benchmark/benchmark.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
+#include <log/log_read.h>
 #include <private/android_logger.h>
 
 BENCHMARK_MAIN();
@@ -183,7 +184,7 @@
    */
 
   struct timespec ts;
-  clock_gettime(android_log_clockid(), &ts);
+  clock_gettime(CLOCK_REALTIME, &ts);
 
   android_pmsg_log_header_t pmsg_header;
   pmsg_header.magic = LOGGER_MAGIC;
@@ -259,7 +260,7 @@
    */
 
   struct timespec ts;
-  clock_gettime(android_log_clockid(), &ts);
+  clock_gettime(CLOCK_REALTIME, &ts);
 
   struct packet {
     android_pmsg_log_header_t pmsg_header;
@@ -334,7 +335,7 @@
    */
 
   struct timespec ts;
-  clock_gettime(android_log_clockid(), &ts);
+  clock_gettime(CLOCK_REALTIME, &ts);
 
   struct packet {
     android_pmsg_log_header_t pmsg_header;
@@ -409,7 +410,7 @@
    */
 
   struct timespec ts;
-  clock_gettime(android_log_clockid(), &ts);
+  clock_gettime(CLOCK_REALTIME, &ts);
 
   struct packet {
     android_pmsg_log_header_t pmsg_header;
@@ -482,7 +483,7 @@
    */
 
   struct timespec ts;
-  clock_gettime(android_log_clockid(), &ts);
+  clock_gettime(CLOCK_REALTIME, &ts);
 
   struct packet {
     android_pmsg_log_header_t pmsg_header;
@@ -791,16 +792,6 @@
 BENCHMARK(BM_is_loggable);
 
 /*
- *	Measure the time it takes for android_log_clockid.
- */
-static void BM_clockid(benchmark::State& state) {
-  while (state.KeepRunning()) {
-    android_log_clockid();
-  }
-}
-BENCHMARK(BM_clockid);
-
-/*
  *	Measure the time it takes for __android_log_security.
  */
 static void BM_security(benchmark::State& state) {
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 048bf61..bbc985a 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -40,6 +40,7 @@
 #include <gtest/gtest.h>
 #include <log/log_event_list.h>
 #include <log/log_properties.h>
+#include <log/log_read.h>
 #include <log/logprint.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
@@ -328,8 +329,6 @@
 #ifdef __ANDROID__
   pid_t pid = getpid();
 
-  log_time ts(android_log_clockid());
-
   size_t num_lines = 1, size = 0, length = 0, total = 0;
   const char* cp = message;
   while (*cp) {
@@ -431,7 +430,6 @@
   pid_t pid = getpid();
 
   static const char tag[] = "TEST__android_log_buf_write";
-  log_time ts(android_log_clockid());
 
   auto write_function = [&] {
     EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO, tag, message));
diff --git a/liblog/tests/logd_writer_test.cpp b/liblog/tests/logd_writer_test.cpp
new file mode 100644
index 0000000..b8e4726
--- /dev/null
+++ b/liblog/tests/logd_writer_test.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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 <sys/un.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+// logd_writer takes advantage of the fact that connect() can be called multiple times for a DGRAM
+// socket.  This tests for that behavior.
+TEST(liblog, multi_connect_dgram_socket) {
+#ifdef __ANDROID__
+  if (getuid() != 0) {
+    GTEST_SKIP() << "Skipping test, must be run as root.";
+    return;
+  }
+  auto temp_dir = TemporaryDir();
+  auto socket_path = StringPrintf("%s/test_socket", temp_dir.path);
+
+  unique_fd server_socket;
+
+  auto open_server_socket = [&] {
+    server_socket.reset(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)));
+    ASSERT_TRUE(server_socket.ok());
+
+    sockaddr_un server_sockaddr = {};
+    server_sockaddr.sun_family = AF_UNIX;
+    strlcpy(server_sockaddr.sun_path, socket_path.c_str(), sizeof(server_sockaddr.sun_path));
+    ASSERT_EQ(0,
+              TEMP_FAILURE_RETRY(bind(server_socket, reinterpret_cast<sockaddr*>(&server_sockaddr),
+                                      sizeof(server_sockaddr))));
+  };
+
+  // Open the server socket.
+  open_server_socket();
+
+  // Open the client socket.
+  auto client_socket =
+      unique_fd{TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0))};
+  ASSERT_TRUE(client_socket.ok());
+  sockaddr_un client_sockaddr = {};
+  client_sockaddr.sun_family = AF_UNIX;
+  strlcpy(client_sockaddr.sun_path, socket_path.c_str(), sizeof(client_sockaddr.sun_path));
+  ASSERT_EQ(0,
+            TEMP_FAILURE_RETRY(connect(client_socket, reinterpret_cast<sockaddr*>(&client_sockaddr),
+                                       sizeof(client_sockaddr))));
+
+  // Ensure that communication works.
+  constexpr static char kSmoke[] = "smoke test";
+  ssize_t smoke_len = sizeof(kSmoke);
+  ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+  char read_buf[512];
+  ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(read(server_socket, read_buf, sizeof(read_buf))));
+  ASSERT_STREQ(kSmoke, read_buf);
+
+  // Close the server socket.
+  server_socket.reset();
+  ASSERT_EQ(0, unlink(socket_path.c_str())) << strerror(errno);
+
+  // Ensure that write() from the client returns an error since the server is closed.
+  ASSERT_EQ(-1, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+  ASSERT_EQ(errno, ECONNREFUSED) << strerror(errno);
+
+  // Open the server socket again.
+  open_server_socket();
+
+  // Reconnect the same client socket.
+  ASSERT_EQ(0,
+            TEMP_FAILURE_RETRY(connect(client_socket, reinterpret_cast<sockaddr*>(&client_sockaddr),
+                                       sizeof(client_sockaddr))))
+      << strerror(errno);
+
+  // Ensure that communication works.
+  ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+  ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(read(server_socket, read_buf, sizeof(read_buf))));
+  ASSERT_STREQ(kSmoke, read_buf);
+#else
+  GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
\ No newline at end of file
diff --git a/liblog/tests/rwlock_test.cpp b/liblog/tests/rwlock_test.cpp
deleted file mode 100644
index 617d5c4..0000000
--- a/liblog/tests/rwlock_test.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "../rwlock.h"
-
-#include <chrono>
-#include <shared_mutex>
-#include <thread>
-
-#include <gtest/gtest.h>
-
-using namespace std::literals;
-
-TEST(rwlock, reader_then_reader_lock) {
-  RwLock lock;
-
-  bool thread_ran = false;
-  auto read_guard = std::shared_lock{lock};
-
-  auto reader_thread = std::thread([&] {
-    auto read_guard = std::shared_lock{lock};
-    thread_ran = true;
-  });
-
-  auto end_time = std::chrono::steady_clock::now() + 1s;
-
-  while (std::chrono::steady_clock::now() < end_time) {
-    if (thread_ran) {
-      break;
-    }
-  }
-
-  EXPECT_EQ(true, thread_ran);
-
-  // Unlock the lock in case something went wrong, to ensure that we can still join() the thread.
-  read_guard.unlock();
-  reader_thread.join();
-}
-
-template <template <typename> typename L1, template <typename> typename L2>
-void TestBlockingLocks() {
-  RwLock lock;
-
-  bool thread_ran = false;
-  auto read_guard = L1{lock};
-
-  auto reader_thread = std::thread([&] {
-    auto read_guard = L2{lock};
-    thread_ran = true;
-  });
-
-  auto end_time = std::chrono::steady_clock::now() + 1s;
-
-  while (std::chrono::steady_clock::now() < end_time) {
-    if (thread_ran) {
-      break;
-    }
-  }
-
-  EXPECT_EQ(false, thread_ran);
-
-  read_guard.unlock();
-  reader_thread.join();
-
-  EXPECT_EQ(true, thread_ran);
-}
-
-TEST(rwlock, reader_then_writer_lock) {
-  TestBlockingLocks<std::shared_lock, std::unique_lock>();
-}
-
-TEST(rwlock, writer_then_reader_lock) {
-  TestBlockingLocks<std::unique_lock, std::shared_lock>();
-}
-
-TEST(rwlock, writer_then_writer_lock) {
-  TestBlockingLocks<std::unique_lock, std::unique_lock>();
-}
diff --git a/libnetutils/packet.c b/libnetutils/packet.c
index 9ecdd4f..64de00e 100644
--- a/libnetutils/packet.c
+++ b/libnetutils/packet.c
@@ -37,25 +37,22 @@
 
 #include "dhcpmsg.h"
 
-int fatal();
+int fatal(const char*);
 
-int open_raw_socket(const char *ifname __attribute__((unused)), uint8_t *hwaddr, int if_index)
-{
-    int s;
-    struct sockaddr_ll bindaddr;
+int open_raw_socket(const char* ifname __unused, uint8_t hwaddr[ETH_ALEN], int if_index) {
+    int s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+    if (s < 0) return fatal("socket(PF_PACKET)");
 
-    if((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
-        return fatal("socket(PF_PACKET)");
-    }
-
-    memset(&bindaddr, 0, sizeof(bindaddr));
-    bindaddr.sll_family = AF_PACKET;
-    bindaddr.sll_protocol = htons(ETH_P_IP);
-    bindaddr.sll_halen = ETH_ALEN;
+    struct sockaddr_ll bindaddr = {
+            .sll_family = AF_PACKET,
+            .sll_protocol = htons(ETH_P_IP),
+            .sll_ifindex = if_index,
+            .sll_halen = ETH_ALEN,
+    };
     memcpy(bindaddr.sll_addr, hwaddr, ETH_ALEN);
-    bindaddr.sll_ifindex = if_index;
 
     if (bind(s, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
+        close(s);
         return fatal("Cannot bind raw socket to interface");
     }
 
diff --git a/libnetutils/packet.h b/libnetutils/packet.h
index aade392..66186fc 100644
--- a/libnetutils/packet.h
+++ b/libnetutils/packet.h
@@ -17,7 +17,9 @@
 #ifndef _WIFI_PACKET_H_
 #define _WIFI_PACKET_H_
 
-int open_raw_socket(const char *ifname, uint8_t *hwaddr, int if_index);
+#include <linux/if_ether.h>
+
+int open_raw_socket(const char* ifname, uint8_t hwaddr[ETH_ALEN], int if_index);
 int send_packet(int s, int if_index, struct dhcp_msg *msg, int size,
                 uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport);
 int receive_packet(int s, struct dhcp_msg *msg);
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 2c1b255..bda11e9 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -17,6 +17,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "29",
 }
 
 cc_library {
@@ -60,4 +61,5 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "29",
 }
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
index 12474f1..ccc6f62 100644
--- a/libprocessgroup/profiles/Android.bp
+++ b/libprocessgroup/profiles/Android.bp
@@ -89,24 +89,23 @@
         "test_vendor.cpp",
     ],
     static_libs: [
+        "libbase",
         "libgmock",
+        "liblog",
+        "libjsoncpp",
         "libjsonpbverify",
         "libjsonpbparse",
         "libprocessgroup_proto",
     ],
     shared_libs: [
-        "libbase",
-        "liblog",
-        "libjsoncpp",
         "libprotobuf-cpp-full",
     ],
-    target: {
-        android: {
-            test_config: "vts_processgroup_validate_test.xml",
-        },
-    },
+    test_suites: [
+        "vts",
+    ],
 }
 
 vts_config {
     name: "VtsProcessgroupValidateTest",
+    test_config: "vts_processgroup_validate_test.xml",
 }
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 3f08535..a515e58 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -144,6 +144,19 @@
         }
       ]
     },
+    {
+      "Name": "CameraServicePerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "camera-daemon"
+          }
+        }
+      ]
+    },
 
     {
       "Name": "CpuPolicySpread",
diff --git a/libstats/push_compat/Android.bp b/libstats/push_compat/Android.bp
index caea048..cbc65ff 100644
--- a/libstats/push_compat/Android.bp
+++ b/libstats/push_compat/Android.bp
@@ -48,6 +48,7 @@
     export_include_dirs: ["include"],
     static_libs: ["libgtest_prod"],
     apex_available: ["com.android.resolv"],
+    min_sdk_version: "29",
 }
 
 cc_test {
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
index 690dc94..6882ab2 100644
--- a/libstats/socket/Android.bp
+++ b/libstats/socket/Android.bp
@@ -55,6 +55,7 @@
     export_include_dirs: ["include"],
     host_supported: true,
     apex_available: ["com.android.resolv"],
+    min_sdk_version: "29",
 }
 
 cc_benchmark {
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 5efe03f..9c1621b 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -180,6 +180,7 @@
     struct ifa_cacheinfo *cacheinfo = nullptr;
     char addrstr[INET6_ADDRSTRLEN] = "";
     char ifname[IFNAMSIZ] = "";
+    uint32_t flags;
 
     if (!checkRtNetlinkLength(nh, sizeof(*ifaddr)))
         return false;
@@ -194,6 +195,9 @@
     // For log messages.
     const char *msgtype = rtMessageName(type);
 
+    // First 8 bits of flags. In practice will always be overridden when parsing IFA_FLAGS below.
+    flags = ifaddr->ifa_flags;
+
     struct rtattr *rta;
     int len = IFA_PAYLOAD(nh);
     for (rta = IFA_RTA(ifaddr); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
@@ -242,6 +246,9 @@
             }
 
             cacheinfo = (struct ifa_cacheinfo *) RTA_DATA(rta);
+
+        } else if (rta->rta_type == IFA_FLAGS) {
+            flags = *(uint32_t*)RTA_DATA(rta);
         }
     }
 
@@ -256,7 +263,7 @@
     mSubsystem = strdup("net");
     asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr, ifaddr->ifa_prefixlen);
     asprintf(&mParams[1], "INTERFACE=%s", ifname);
-    asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags);
+    asprintf(&mParams[2], "FLAGS=%u", flags);
     asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope);
     asprintf(&mParams[4], "IFINDEX=%u", ifaddr->ifa_index);
 
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index bf7d69e..f3d3f27 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -75,13 +75,29 @@
     ],
 
     target: {
-        // Always disable optimizations for host to make it easier to debug.
         host: {
+            // Always disable optimizations for host to make it easier to debug.
             cflags: [
                 "-O0",
                 "-g",
             ],
         },
+        android: {
+            header_libs: ["bionic_libc_platform_headers"],
+            product_variables: {
+                experimental_mte: {
+                    cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+                },
+            },
+        },
+        linux_bionic: {
+            header_libs: ["bionic_libc_platform_headers"],
+            product_variables: {
+                experimental_mte: {
+                    cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+                },
+            },
+        },
     },
 
     arch: {
@@ -102,16 +118,6 @@
         "liblog",
         "liblzma",
     ],
-
-    header_libs: [
-        "bionic_libc_platform_headers",
-    ],
-
-    product_variables: {
-        experimental_mte: {
-            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
-        },
-    },
 }
 
 cc_library {
@@ -281,13 +287,22 @@
         "tests/files/offline/straddle_arm64/*",
     ],
 
-    header_libs: [
-        "bionic_libc_platform_headers",
-    ],
-
-    product_variables: {
-        experimental_mte: {
-            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+    target: {
+        android: {
+            header_libs: ["bionic_libc_platform_headers"],
+            product_variables: {
+                experimental_mte: {
+                    cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+                },
+            },
+        },
+        linux_bionic: {
+            header_libs: ["bionic_libc_platform_headers"],
+            product_variables: {
+                experimental_mte: {
+                    cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+                },
+            },
         },
     },
 }
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index f01b092..286febc 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -124,6 +124,12 @@
     return false;
   }
 
+  if (arch() == ARCH_ARM64) {
+    // Tagged pointer after Android R would lead top byte to have random values
+    // https://source.android.com/devices/tech/debug/tagged-pointers
+    vaddr &= (1ULL << 56) - 1;
+  }
+
   // Check the .data section.
   uint64_t vaddr_start = interface_->data_vaddr_start();
   if (vaddr >= vaddr_start && vaddr < interface_->data_vaddr_end()) {
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 8f49ad9..670d904 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -159,6 +159,8 @@
         search_map_idx = old_map_idx + 1;
         if (new_map_idx + 1 < maps_.size()) {
           maps_[new_map_idx + 1]->prev_map = info.get();
+          maps_[new_map_idx + 1]->prev_real_map =
+              info->IsBlank() ? info->prev_real_map : info.get();
         }
         maps_[new_map_idx] = nullptr;
         total_entries--;
diff --git a/libunwindstack/MemoryMte.cpp b/libunwindstack/MemoryMte.cpp
index d1d0ebc..46a546e 100644
--- a/libunwindstack/MemoryMte.cpp
+++ b/libunwindstack/MemoryMte.cpp
@@ -17,6 +17,7 @@
 #if defined(ANDROID_EXPERIMENTAL_MTE)
 
 #include <sys/ptrace.h>
+#include <sys/uio.h>
 
 #include <bionic/mte.h>
 #include <bionic/mte_kernel.h>
@@ -28,7 +29,13 @@
 
 long MemoryRemote::ReadTag(uint64_t addr) {
 #if defined(__aarch64__)
-  return ptrace(PTRACE_PEEKTAG, pid_, (void*)addr, nullptr);
+  char tag;
+  iovec iov = {&tag, 1};
+  if (ptrace(PTRACE_PEEKMTETAGS, pid_, reinterpret_cast<void*>(addr), &iov) != 0 ||
+      iov.iov_len != 1) {
+    return -1;
+  }
+  return tag;
 #else
   (void)addr;
   return -1;
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index fc90dab..3b6cb80 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -55,6 +55,8 @@
 
   void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; }
 
+  void FakeSetArch(ArchEnum arch) { arch_ = arch; }
+
   void FakeSetInterface(ElfInterface* interface) { interface_.reset(interface); }
   void FakeSetGnuDebugdataInterface(ElfInterface* interface) {
     gnu_debugdata_interface_.reset(interface);
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 1f3ed81..f0852a4 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -438,6 +438,48 @@
   EXPECT_EQ(0xc080U, offset);
 }
 
+TEST_F(ElfTest, get_global_vaddr_with_tagged_pointer) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetArch(ARCH_ARM64);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+  interface->MockSetDataVaddrStart(0x500);
+  interface->MockSetDataVaddrEnd(0x600);
+  interface->MockSetDataOffset(0xa000);
+
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, ::testing::_))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x8800000000000580),
+                                 ::testing::Return(true)));
+
+  uint64_t offset;
+  ASSERT_TRUE(elf.GetGlobalVariableOffset(global, &offset));
+  EXPECT_EQ(0xa080U, offset);
+}
+
+TEST_F(ElfTest, get_global_vaddr_without_tagged_pointer) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetArch(ARCH_X86_64);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+  interface->MockSetDataVaddrStart(0x8800000000000500);
+  interface->MockSetDataVaddrEnd(0x8800000000000600);
+  interface->MockSetDataOffset(0x880000000000a000);
+
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, ::testing::_))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x8800000000000580),
+                                 ::testing::Return(true)));
+
+  uint64_t offset;
+  ASSERT_TRUE(elf.GetGlobalVariableOffset(global, &offset));
+  EXPECT_EQ(0x880000000000a080U, offset);
+}
+
 TEST_F(ElfTest, is_valid_pc_elf_invalid) {
   ElfFake elf(memory_);
   elf.FakeSetValid(false);
diff --git a/libunwindstack/tests/LocalUpdatableMapsTest.cpp b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
index b816b9a..99afb0b 100644
--- a/libunwindstack/tests/LocalUpdatableMapsTest.cpp
+++ b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
@@ -271,4 +271,103 @@
   EXPECT_TRUE(map_info->name.empty());
 }
 
+TEST_F(LocalUpdatableMapsTest, add_map_prev_name_updated) {
+  TemporaryFile tf;
+  ASSERT_TRUE(
+      android::base::WriteStringToFile("3000-4000 rwxp 00000 00:00 0\n"
+                                       "8000-9000 r-xp 00000 00:00 0\n"
+                                       "9000-a000 r-xp 00000 00:00 0\n",
+                                       tf.path));
+
+  maps_.TestSetMapsFile(tf.path);
+  ASSERT_TRUE(maps_.Reparse());
+  ASSERT_EQ(3U, maps_.Total());
+
+  MapInfo* map_info = maps_.Get(2);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x9000U, map_info->start);
+  EXPECT_EQ(0xA000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_TRUE(map_info->name.empty());
+  EXPECT_EQ(maps_.Get(1), map_info->prev_map);
+}
+
+TEST_F(LocalUpdatableMapsTest, add_map_prev_real_name_updated) {
+  TemporaryFile tf;
+  ASSERT_TRUE(
+      android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
+                                       "4000-5000 ---p 00000 00:00 0\n"
+                                       "7000-8000 r-xp 00000 00:00 0 /fake/lib1.so\n"
+                                       "8000-9000 ---p 00000 00:00 0\n",
+                                       tf.path));
+
+  maps_.TestSetMapsFile(tf.path);
+  ASSERT_TRUE(maps_.Reparse());
+  ASSERT_EQ(4U, maps_.Total());
+
+  MapInfo* map_info = maps_.Get(2);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x7000U, map_info->start);
+  EXPECT_EQ(0x8000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_EQ(maps_.Get(0), map_info->prev_real_map);
+  EXPECT_EQ(maps_.Get(1), map_info->prev_map);
+  EXPECT_EQ("/fake/lib1.so", map_info->name);
+
+  map_info = maps_.Get(3);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x8000U, map_info->start);
+  EXPECT_EQ(0x9000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_TRUE(map_info->IsBlank());
+  EXPECT_EQ(maps_.Get(2), map_info->prev_real_map);
+  EXPECT_EQ(maps_.Get(2), map_info->prev_map);
+  EXPECT_TRUE(map_info->name.empty());
+
+  ASSERT_TRUE(
+      android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
+                                       "4000-5000 ---p 00000 00:00 0\n"
+                                       "7000-8000 r-xp 00000 00:00 0 /fake/lib1.so\n"
+                                       "8000-9000 ---p 00000 00:00 0\n"
+                                       "9000-a000 r-xp 00000 00:00 0 /fake/lib2.so\n"
+                                       "a000-b000 r-xp 00000 00:00 0 /fake/lib3.so\n",
+                                       tf.path));
+
+  maps_.TestSetMapsFile(tf.path);
+  ASSERT_TRUE(maps_.Reparse());
+  ASSERT_EQ(6U, maps_.Total());
+
+  map_info = maps_.Get(2);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x7000U, map_info->start);
+  EXPECT_EQ(0x8000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_EQ("/fake/lib1.so", map_info->name);
+  EXPECT_EQ(maps_.Get(1), map_info->prev_map);
+  EXPECT_EQ(maps_.Get(0), map_info->prev_real_map);
+
+  map_info = maps_.Get(4);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0x9000U, map_info->start);
+  EXPECT_EQ(0xA000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_EQ("/fake/lib2.so", map_info->name);
+  EXPECT_EQ(maps_.Get(3), map_info->prev_map);
+  EXPECT_EQ(maps_.Get(2), map_info->prev_real_map);
+
+  map_info = maps_.Get(5);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_EQ(0xA000U, map_info->start);
+  EXPECT_EQ(0xB000U, map_info->end);
+  EXPECT_EQ(0U, map_info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+  EXPECT_EQ("/fake/lib3.so", map_info->name);
+  EXPECT_EQ(maps_.Get(4), map_info->prev_map);
+  EXPECT_EQ(maps_.Get(4), map_info->prev_real_map);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index 385078d..621893b 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -28,8 +28,6 @@
 
 #include <android-base/file.h>
 #include <android-base/test_utils.h>
-#include <bionic/mte.h>
-#include <bionic/mte_kernel.h>
 #include <gtest/gtest.h>
 
 #include "MemoryRemote.h"
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 1202c15..1d899ab 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -195,7 +195,7 @@
     int prot = PROT_READ;
     if (!readOnly) prot |= PROT_WRITE;
 
-    void* ptr = mmap(nullptr, adjLength, prot, flags, fd, adjOffset);
+    void* ptr = mmap64(nullptr, adjLength, prot, flags, fd, adjOffset);
     if (ptr == MAP_FAILED) {
         if (errno == EINVAL && length == 0) {
             ptr = nullptr;
diff --git a/libutils/FileMap_test.cpp b/libutils/FileMap_test.cpp
index 576d89b..9f7ce85 100644
--- a/libutils/FileMap_test.cpp
+++ b/libutils/FileMap_test.cpp
@@ -32,3 +32,23 @@
     ASSERT_EQ(0u, m.getDataLength());
     ASSERT_EQ(4096, m.getDataOffset());
 }
+
+TEST(FileMap, large_offset) {
+    // Make sure that an offset > INT32_MAX will not fail the create
+    // function. See http://b/155662887.
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+
+    off64_t offset = INT32_MAX + 1024LL;
+
+    // Make the temporary file large enough to pass the mmap.
+    ASSERT_EQ(offset, lseek64(tf.fd, offset, SEEK_SET));
+    char value = 0;
+    ASSERT_EQ(1, write(tf.fd, &value, 1));
+
+    android::FileMap m;
+    ASSERT_TRUE(m.create("test", tf.fd, offset, 0, true));
+    ASSERT_STREQ("test", m.getFileName());
+    ASSERT_EQ(0u, m.getDataLength());
+    ASSERT_EQ(offset, m.getDataOffset());
+}
diff --git a/libutils/include/utils/Compat.h b/libutils/include/utils/Compat.h
index dee577e..6002567 100644
--- a/libutils/include/utils/Compat.h
+++ b/libutils/include/utils/Compat.h
@@ -19,12 +19,20 @@
 
 #include <unistd.h>
 
+#if !defined(__MINGW32__)
+#include <sys/mman.h>
+#endif
+
 #if defined(__APPLE__)
 
 /* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
-
+static_assert(sizeof(off_t) >= 8, "This code requires that Mac OS have at least a 64-bit off_t.");
 typedef off_t off64_t;
 
+static inline void* mmap64(void* addr, size_t length, int prot, int flags, int fd, off64_t offset) {
+    return mmap(addr, length, prot, flags, fd, offset);
+}
+
 static inline off64_t lseek64(int fd, off64_t offset, int whence) {
     return lseek(fd, offset, whence);
 }
diff --git a/libutils/include/utils/Looper.h b/libutils/include/utils/Looper.h
index c439c5c..466fbb7 100644
--- a/libutils/include/utils/Looper.h
+++ b/libutils/include/utils/Looper.h
@@ -26,6 +26,8 @@
 
 #include <android-base/unique_fd.h>
 
+#include <utility>
+
 namespace android {
 
 /*
@@ -438,9 +440,8 @@
     struct MessageEnvelope {
         MessageEnvelope() : uptime(0) { }
 
-        MessageEnvelope(nsecs_t u, const sp<MessageHandler> h,
-                const Message& m) : uptime(u), handler(h), message(m) {
-        }
+        MessageEnvelope(nsecs_t u, sp<MessageHandler> h, const Message& m)
+            : uptime(u), handler(std::move(h)), message(m) {}
 
         nsecs_t uptime;
         sp<MessageHandler> handler;
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 1c3acb8..8ad9900 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -41,6 +41,7 @@
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -1204,9 +1205,19 @@
                 }
             }
             // We are here because we have confirmed kernel live-lock
+            std::vector<std::string> threads;
+            auto taskdir = procdir + std::to_string(tid) + "/task/";
+            dir taskDirectory(taskdir);
+            for (auto tp = taskDirectory.read(); tp != nullptr; tp = taskDirectory.read()) {
+                std::string piddir;
+                if (getValidTidDir(tp, &piddir))
+                    threads.push_back(android::base::Basename(piddir));
+            }
             const auto message = state + " "s + llkFormat(procp->count) + " " +
                                  std::to_string(ppid) + "->" + std::to_string(pid) + "->" +
-                                 std::to_string(tid) + " " + process_comm + " [panic]";
+                                 std::to_string(tid) + " " + process_comm + " [panic]\n" +
+                                 "  thread group: {" + android::base::Join(threads, ",") +
+                                 "}";
             llkPanicKernel(dump, tid,
                            (state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping",
                            message);
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index b065855..13023f2 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -50,6 +50,7 @@
 #include <android/log.h>
 #include <log/event_tag_map.h>
 #include <log/log_id.h>
+#include <log/log_read.h>
 #include <log/logprint.h>
 #include <private/android_logger.h>
 #include <processgroup/sched_policy.h>
@@ -122,6 +123,18 @@
     return fd;
 }
 
+static void closeLogFile(const char* pathname) {
+    int fd = open(pathname, O_WRONLY | O_CLOEXEC);
+    if (fd == -1) {
+        return;
+    }
+
+    // no need to check errors
+    __u32 set = 0;
+    ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set);
+    close(fd);
+}
+
 void Logcat::RotateLogs() {
     // Can't rotate logs if we're not outputting to a file
     if (!output_file_name_) return;
@@ -152,6 +165,8 @@
             break;
         }
 
+        closeLogFile(file0.c_str());
+
         int err = rename(file0.c_str(), file1.c_str());
 
         if (err < 0 && errno != ENOENT) {
@@ -444,7 +459,7 @@
                                             closedir);
     if (!dir.get()) return retval;
 
-    log_time now(android_log_clockid());
+    log_time now(CLOCK_REALTIME);
 
     size_t len = strlen(file);
     log_time modulo(0, NS_PER_SEC);
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index b32b437..3a55c4e 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -174,11 +174,6 @@
 }
 
 TEST(logcat, year) {
-    if (android_log_clockid() == CLOCK_MONOTONIC) {
-        fprintf(stderr, "Skipping test, logd is monotonic time\n");
-        return;
-    }
-
     int count;
     int tries = 3;  // in case run too soon after system start or buffer clear
 
@@ -249,11 +244,6 @@
 }
 
 TEST(logcat, tz) {
-    if (android_log_clockid() == CLOCK_MONOTONIC) {
-        fprintf(stderr, "Skipping test, logd is monotonic time\n");
-        return;
-    }
-
     int tries = 4;  // in case run too soon after system start or buffer clear
     int count;
 
diff --git a/logd/Android.bp b/logd/Android.bp
index b337b7c..ea1054d 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -36,10 +36,9 @@
         "CommandListener.cpp",
         "LogListener.cpp",
         "LogReader.cpp",
-        "FlushCommand.cpp",
+        "LogReaderThread.cpp",
         "LogBuffer.cpp",
         "LogBufferElement.cpp",
-        "LogTimes.cpp",
         "LogStatistics.cpp",
         "LogWhiteBlackList.cpp",
         "libaudit.c",
@@ -53,7 +52,9 @@
 
     export_include_dirs: ["."],
 
-    cflags: ["-Werror"] + event_flag,
+    cflags: [
+        "-Wextra",
+    ] + event_flag,
 }
 
 cc_binary {
@@ -76,7 +77,9 @@
         "libcap",
     ],
 
-    cflags: ["-Werror"],
+    cflags: [
+        "-Wextra",
+    ],
 }
 
 cc_binary {
@@ -91,10 +94,8 @@
     shared_libs: ["libbase"],
 
     cflags: [
-        "-Wall",
+        "-Wconversion",
         "-Wextra",
-        "-Werror",
-        "-Wconversion"
     ],
 }
 
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 694b5fa..4044dc9 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -31,6 +31,7 @@
 
 #include <android-base/stringprintf.h>
 #include <cutils/sockets.h>
+#include <log/log_properties.h>
 #include <private/android_filesystem_config.h>
 #include <sysutils/SocketClient.h>
 
@@ -38,37 +39,20 @@
 #include "LogCommand.h"
 #include "LogUtils.h"
 
-CommandListener::CommandListener(LogBuffer* buf, LogReader* /*reader*/,
-                                 LogListener* /*swl*/)
-    : FrameworkListener(getLogSocket()) {
-    // registerCmd(new ShutdownCmd(buf, writer, swl));
-    registerCmd(new ClearCmd(buf));
-    registerCmd(new GetBufSizeCmd(buf));
-    registerCmd(new SetBufSizeCmd(buf));
-    registerCmd(new GetBufSizeUsedCmd(buf));
-    registerCmd(new GetStatisticsCmd(buf));
-    registerCmd(new SetPruneListCmd(buf));
-    registerCmd(new GetPruneListCmd(buf));
-    registerCmd(new GetEventTagCmd(buf));
-    registerCmd(new ReinitCmd());
+CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune)
+    : FrameworkListener(getLogSocket()), buf_(buf), tags_(tags), prune_(prune) {
+    registerCmd(new ClearCmd(this));
+    registerCmd(new GetBufSizeCmd(this));
+    registerCmd(new SetBufSizeCmd(this));
+    registerCmd(new GetBufSizeUsedCmd(this));
+    registerCmd(new GetStatisticsCmd(this));
+    registerCmd(new SetPruneListCmd(this));
+    registerCmd(new GetPruneListCmd(this));
+    registerCmd(new GetEventTagCmd(this));
+    registerCmd(new ReinitCmd(this));
     registerCmd(new ExitCmd(this));
 }
 
-CommandListener::ShutdownCmd::ShutdownCmd(LogReader* reader, LogListener* swl)
-    : LogCommand("shutdown"), mReader(*reader), mSwl(*swl) {
-}
-
-int CommandListener::ShutdownCmd::runCommand(SocketClient* /*cli*/,
-                                             int /*argc*/, char** /*argv*/) {
-    mSwl.stopListener();
-    mReader.stopListener();
-    exit(0);
-}
-
-CommandListener::ClearCmd::ClearCmd(LogBuffer* buf)
-    : LogCommand("clear"), mBuf(*buf) {
-}
-
 static void setname() {
     static bool name_set;
     if (!name_set) {
@@ -96,14 +80,10 @@
         return 0;
     }
 
-    cli->sendMsg(mBuf.clear((log_id_t)id, uid) ? "busy" : "success");
+    cli->sendMsg(buf()->clear((log_id_t)id, uid) ? "busy" : "success");
     return 0;
 }
 
-CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer* buf)
-    : LogCommand("getLogSize"), mBuf(*buf) {
-}
-
 int CommandListener::GetBufSizeCmd::runCommand(SocketClient* cli, int argc,
                                                char** argv) {
     setname();
@@ -118,17 +98,13 @@
         return 0;
     }
 
-    unsigned long size = mBuf.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);
     return 0;
 }
 
-CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer* buf)
-    : LogCommand("setLogSize"), mBuf(*buf) {
-}
-
 int CommandListener::SetBufSizeCmd::runCommand(SocketClient* cli, int argc,
                                                char** argv) {
     setname();
@@ -149,7 +125,7 @@
     }
 
     unsigned long size = atol(argv[2]);
-    if (mBuf.setSize((log_id_t)id, size)) {
+    if (buf()->setSize((log_id_t)id, size)) {
         cli->sendMsg("Range Error");
         return 0;
     }
@@ -158,10 +134,6 @@
     return 0;
 }
 
-CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer* buf)
-    : LogCommand("getLogSizeUsed"), mBuf(*buf) {
-}
-
 int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient* cli, int argc,
                                                    char** argv) {
     setname();
@@ -176,17 +148,13 @@
         return 0;
     }
 
-    unsigned long size = mBuf.getSizeUsed((log_id_t)id);
+    unsigned long size = buf()->getSizeUsed((log_id_t)id);
     char buf[512];
     snprintf(buf, sizeof(buf), "%lu", size);
     cli->sendMsg(buf);
     return 0;
 }
 
-CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer* buf)
-    : LogCommand("getStatistics"), mBuf(*buf) {
-}
-
 // This returns a string with a length prefix with the format <length>\n<data>\n\f.  The length
 // prefix includes the length of the prefix itself.
 static std::string PackageString(const std::string& str) {
@@ -241,25 +209,17 @@
         }
     }
 
-    cli->sendMsg(PackageString(mBuf.formatStatistics(uid, pid, logMask)).c_str());
+    cli->sendMsg(PackageString(buf()->formatStatistics(uid, pid, logMask)).c_str());
     return 0;
 }
 
-CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer* buf)
-    : LogCommand("getPruneList"), mBuf(*buf) {
-}
-
 int CommandListener::GetPruneListCmd::runCommand(SocketClient* cli,
                                                  int /*argc*/, char** /*argv*/) {
     setname();
-    cli->sendMsg(PackageString(mBuf.formatPrune()).c_str());
+    cli->sendMsg(PackageString(prune()->format()).c_str());
     return 0;
 }
 
-CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer* buf)
-    : LogCommand("setPruneList"), mBuf(*buf) {
-}
-
 int CommandListener::SetPruneListCmd::runCommand(SocketClient* cli, int argc,
                                                  char** argv) {
     setname();
@@ -276,7 +236,7 @@
         str += argv[i];
     }
 
-    int ret = mBuf.initPrune(str.c_str());
+    int ret = prune()->init(str.c_str());
 
     if (ret) {
         cli->sendMsg("Invalid");
@@ -288,10 +248,6 @@
     return 0;
 }
 
-CommandListener::GetEventTagCmd::GetEventTagCmd(LogBuffer* buf)
-    : LogCommand("getEventTag"), mBuf(*buf) {
-}
-
 int CommandListener::GetEventTagCmd::runCommand(SocketClient* cli, int argc,
                                                 char** argv) {
     setname();
@@ -328,39 +284,45 @@
             cli->sendMsg("can not mix id= with either format= or name=");
             return 0;
         }
-        cli->sendMsg(PackageString(mBuf.formatEntry(atoi(id), uid)).c_str());
+        cli->sendMsg(PackageString(tags()->formatEntry(atoi(id), uid)).c_str());
         return 0;
     }
 
-    cli->sendMsg(PackageString(mBuf.formatGetEventTag(uid, name, format)).c_str());
+    cli->sendMsg(PackageString(tags()->formatGetEventTag(uid, name, format)).c_str());
 
     return 0;
 }
 
-CommandListener::ReinitCmd::ReinitCmd() : LogCommand("reinit") {
-}
-
 int CommandListener::ReinitCmd::runCommand(SocketClient* cli, int /*argc*/,
                                            char** /*argv*/) {
     setname();
 
-    reinit_signal_handler(SIGHUP);
+    android::prdebug("logd reinit");
+    buf()->init();
+    prune()->init(nullptr);
+
+    // This only works on userdebug and eng devices to re-read the
+    // /data/misc/logd/event-log-tags file right after /data is mounted.
+    // The operation is near to boot and should only happen once.  There
+    // are races associated with its use since it can trigger a Rebuild
+    // of the file, but that is a can-not-happen since the file was not
+    // read yet.  More dangerous if called later, but if all is well it
+    // should just skip over everything and not write any new entries.
+    if (__android_log_is_debuggable()) {
+        tags()->ReadFileEventLogTags(tags()->debug_event_log_tags);
+    }
 
     cli->sendMsg("success");
 
     return 0;
 }
 
-CommandListener::ExitCmd::ExitCmd(CommandListener* parent)
-    : LogCommand("EXIT"), mParent(*parent) {
-}
-
 int CommandListener::ExitCmd::runCommand(SocketClient* cli, int /*argc*/,
                                          char** /*argv*/) {
     setname();
 
     cli->sendMsg("success");
-    release(cli);
+    parent_->release(cli);
 
     return 0;
 }
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index ed99419..c90c247 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -14,85 +14,53 @@
  * limitations under the License.
  */
 
-#ifndef _COMMANDLISTENER_H__
-#define _COMMANDLISTENER_H__
+#pragma once
 
 #include <sysutils/FrameworkListener.h>
+
 #include "LogBuffer.h"
 #include "LogCommand.h"
 #include "LogListener.h"
 #include "LogReader.h"
-
-// See main.cpp for implementation
-void reinit_signal_handler(int /*signal*/);
+#include "LogTags.h"
+#include "LogWhiteBlackList.h"
 
 class CommandListener : public FrameworkListener {
-   public:
-    CommandListener(LogBuffer* buf, LogReader* reader, LogListener* swl);
-    virtual ~CommandListener() {
-    }
+  public:
+    CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune);
+    virtual ~CommandListener() {}
 
-   private:
+  private:
     static int getLogSocket();
 
-    class ShutdownCmd : public LogCommand {
-        LogReader& mReader;
-        LogListener& mSwl;
+    LogBuffer* buf_;
+    LogTags* tags_;
+    PruneList* prune_;
 
-       public:
-        ShutdownCmd(LogReader* reader, LogListener* swl);
-        virtual ~ShutdownCmd() {
-        }
-        int runCommand(SocketClient* c, int argc, char** argv);
-    };
-
-#define LogBufferCmd(name)                                      \
+#define LogCmd(name, command_string)                            \
     class name##Cmd : public LogCommand {                       \
-        LogBuffer& mBuf;                                        \
+      public:                                                   \
+        explicit name##Cmd(CommandListener* parent)             \
+            : LogCommand(#command_string), parent_(parent) {}   \
+        virtual ~name##Cmd() {}                                 \
+        int runCommand(SocketClient* c, int argc, char** argv); \
                                                                 \
-       public:                                                  \
-        explicit name##Cmd(LogBuffer* buf);                     \
-        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_; }    \
+        CommandListener* parent_;                               \
     }
 
-    LogBufferCmd(Clear);
-    LogBufferCmd(GetBufSize);
-    LogBufferCmd(SetBufSize);
-    LogBufferCmd(GetBufSizeUsed);
-    LogBufferCmd(GetStatistics);
-    LogBufferCmd(GetPruneList);
-    LogBufferCmd(SetPruneList);
-    LogBufferCmd(GetEventTag);
-
-#define LogCmd(name)                                            \
-    class name##Cmd : public LogCommand {                       \
-       public:                                                  \
-        name##Cmd();                                            \
-        virtual ~name##Cmd() {                                  \
-        }                                                       \
-        int runCommand(SocketClient* c, int argc, char** argv); \
-    }
-
-    LogCmd(Reinit);
-
-#define LogParentCmd(name)                                      \
-    class name##Cmd : public LogCommand {                       \
-        CommandListener& mParent;                               \
-                                                                \
-       public:                                                  \
-        name##Cmd();                                            \
-        explicit name##Cmd(CommandListener* parent);            \
-        virtual ~name##Cmd() {                                  \
-        }                                                       \
-        int runCommand(SocketClient* c, int argc, char** argv); \
-        void release(SocketClient* c) {                         \
-            mParent.release(c);                                 \
-        }                                                       \
-    }
-
-    LogParentCmd(Exit);
+    LogCmd(Clear, clear);
+    LogCmd(GetBufSize, getLogSize);
+    LogCmd(SetBufSize, setLogSize);
+    LogCmd(GetBufSizeUsed, getLogSizeUsed);
+    LogCmd(GetStatistics, getStatistics);
+    LogCmd(GetPruneList, getPruneList);
+    LogCmd(SetPruneList, setPruneList);
+    LogCmd(GetEventTag, getEventTag);
+    LogCmd(Reinit, reinit);
+    LogCmd(Exit, EXIT);
+#undef LogCmd
 };
-
-#endif
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
deleted file mode 100644
index 0845504..0000000
--- a/logd/FlushCommand.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdlib.h>
-
-#include <private/android_filesystem_config.h>
-
-#include "FlushCommand.h"
-#include "LogBuffer.h"
-#include "LogBufferElement.h"
-#include "LogCommand.h"
-#include "LogReader.h"
-#include "LogTimes.h"
-#include "LogUtils.h"
-
-// runSocketCommand is called once for every open client on the
-// log reader socket. Here we manage and associated the reader
-// client tracking and log region locks LastLogTimes list of
-// LogTimeEntrys, and spawn a transitory per-client thread to
-// work at filing data to the  socket.
-//
-// global LogTimeEntry::wrlock() is used to protect access,
-// reference counts are used to ensure that individual
-// LogTimeEntry lifetime is managed when not protected.
-void FlushCommand::runSocketCommand(SocketClient* client) {
-    LogTimeEntry* entry = nullptr;
-    LastLogTimes& times = mReader.logbuf().mTimes;
-
-    LogTimeEntry::wrlock();
-    LastLogTimes::iterator it = times.begin();
-    while (it != times.end()) {
-        entry = it->get();
-        if (entry->mClient == client) {
-            if (!entry->isWatchingMultiple(mLogMask)) {
-                LogTimeEntry::unlock();
-                return;
-            }
-            if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) {
-                LogTimeEntry::unlock();
-                return;
-            }
-            entry->triggerReader_Locked();
-            LogTimeEntry::unlock();
-            return;
-        }
-        it++;
-    }
-
-    LogTimeEntry::unlock();
-}
-
-bool FlushCommand::hasReadLogs(SocketClient* client) {
-    return clientHasLogCredentials(client);
-}
-
-static bool clientHasSecurityCredentials(SocketClient* client) {
-    return (client->getUid() == AID_SYSTEM) || (client->getGid() == AID_SYSTEM);
-}
-
-bool FlushCommand::hasSecurityLogs(SocketClient* client) {
-    return clientHasSecurityCredentials(client);
-}
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
deleted file mode 100644
index a69d439..0000000
--- a/logd/FlushCommand.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2012-2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef _FLUSH_COMMAND_H
-#define _FLUSH_COMMAND_H
-
-#include <android/log.h>
-#include <sysutils/SocketClientCommand.h>
-
-class LogBufferElement;
-
-#include "LogTimes.h"
-
-class LogReader;
-
-class FlushCommand : public SocketClientCommand {
-    LogReader& mReader;
-    log_mask_t mLogMask;
-
-   public:
-    explicit FlushCommand(LogReader& reader, log_mask_t logMask)
-        : mReader(reader), mLogMask(logMask) {
-    }
-
-    virtual void runSocketCommand(SocketClient* client);
-
-    static bool hasReadLogs(SocketClient* client);
-    static bool hasSecurityLogs(SocketClient* client);
-};
-
-#endif
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index d9cc0db..bcc187d 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -247,22 +247,10 @@
 
     static const char audit_str[] = " audit(";
     char* timeptr = strstr(str, audit_str);
-    if (timeptr &&
-        ((cp = now.strptime(timeptr + sizeof(audit_str) - 1, "%s.%q"))) &&
+    if (timeptr && ((cp = now.strptime(timeptr + sizeof(audit_str) - 1, "%s.%q"))) &&
         (*cp == ':')) {
         memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3);
         memmove(timeptr + sizeof(audit_str) - 1 + 3, cp, strlen(cp) + 1);
-        if (!isMonotonic()) {
-            if (android::isMonotonic(now)) {
-                LogKlog::convertMonotonicToReal(now);
-            }
-        } else {
-            if (!android::isMonotonic(now)) {
-                LogKlog::convertRealToMonotonic(now);
-            }
-        }
-    } else if (isMonotonic()) {
-        now = log_time(CLOCK_MONOTONIC);
     } else {
         now = log_time(CLOCK_REALTIME);
     }
@@ -277,7 +265,7 @@
                   : LOGGER_ENTRY_MAX_PAYLOAD;
     size_t message_len = str_len + sizeof(android_log_event_string_t);
 
-    log_mask_t notify = 0;
+    unsigned int notify = 0;
 
     if (events) {  // begin scope for event buffer
         uint32_t buffer[(message_len + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index c3d7a3e..7df0a5d 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -36,9 +36,6 @@
    public:
     LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg);
     int log(char* buf, size_t len);
-    bool isMonotonic() {
-        return logbuf->isMonotonic();
-    }
 
    protected:
     virtual bool onDataAvailable(SocketClient* cli);
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 1f8ad05..2cb0c5e 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -46,74 +46,25 @@
 
 void LogBuffer::init() {
     log_id_for_each(i) {
-        mLastSet[i] = false;
-        mLast[i] = mLogElements.begin();
-
         if (setSize(i, __android_logger_get_buffer_size(i))) {
             setSize(i, LOG_BUFFER_MIN_SIZE);
         }
     }
-    bool lastMonotonic = monotonic;
-    monotonic = android_log_clockid() == CLOCK_MONOTONIC;
-    if (lastMonotonic != monotonic) {
-        //
-        // Fixup all timestamps, may not be 100% accurate, but better than
-        // throwing what we have away when we get 'surprised' by a change.
-        // In-place element fixup so no need to check reader-lock. Entries
-        // should already be in timestamp order, but we could end up with a
-        // few out-of-order entries if new monotonics come in before we
-        // are notified of the reinit change in status. A Typical example would
-        // be:
-        //  --------- beginning of system
-        //      10.494082   184   201 D Cryptfs : Just triggered post_fs_data
-        //  --------- beginning of kernel
-        //       0.000000     0     0 I         : Initializing cgroup subsys
-        // as the act of mounting /data would trigger persist.logd.timestamp to
-        // be corrected. 1/30 corner case YMMV.
-        //
-        rdlock();
-        LogBufferElementCollection::iterator it = mLogElements.begin();
-        while ((it != mLogElements.end())) {
-            LogBufferElement* e = *it;
-            if (monotonic) {
-                if (!android::isMonotonic(e->mRealTime)) {
-                    LogKlog::convertRealToMonotonic(e->mRealTime);
-                    if ((e->mRealTime.tv_nsec % 1000) == 0) {
-                        e->mRealTime.tv_nsec++;
-                    }
-                }
-            } else {
-                if (android::isMonotonic(e->mRealTime)) {
-                    LogKlog::convertMonotonicToReal(e->mRealTime);
-                    if ((e->mRealTime.tv_nsec % 1000) == 0) {
-                        e->mRealTime.tv_nsec++;
-                    }
-                }
-            }
-            ++it;
-        }
-        unlock();
-    }
-
-    // We may have been triggered by a SIGHUP. Release any sleeping reader
-    // threads to dump their current content.
-    //
-    // NB: this is _not_ performed in the context of a SIGHUP, it is
-    // performed during startup, and in context of reinit administrative thread
-    LogTimeEntry::wrlock();
+    // Release any sleeping reader threads to dump their current content.
+    LogReaderThread::wrlock();
 
     LastLogTimes::iterator times = mTimes.begin();
     while (times != mTimes.end()) {
-        LogTimeEntry* entry = times->get();
+        LogReaderThread* entry = times->get();
         entry->triggerReader_Locked();
         times++;
     }
 
-    LogTimeEntry::unlock();
+    LogReaderThread::unlock();
 }
 
-LogBuffer::LogBuffer(LastLogTimes* times)
-    : monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
+LogBuffer::LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune)
+    : mTimes(*times), tags_(tags), prune_(prune) {
     pthread_rwlock_init(&mLogElementsLock, nullptr);
 
     log_id_for_each(i) {
@@ -131,6 +82,20 @@
     }
 }
 
+LogBufferElementCollection::iterator LogBuffer::GetOldest(log_id_t log_id) {
+    auto it = mLogElements.begin();
+    if (oldest_[log_id]) {
+        it = *oldest_[log_id];
+    }
+    while (it != mLogElements.end() && (*it)->getLogId() != log_id) {
+        it++;
+    }
+    if (it != mLogElements.end()) {
+        oldest_[log_id] = it;
+    }
+    return it;
+}
+
 enum match_type { DIFFERENT, SAME, SAME_LIBLOG };
 
 static enum match_type identical(LogBufferElement* elem,
@@ -221,7 +186,7 @@
     const char* tag = nullptr;
     size_t tag_len = 0;
     if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
-        tag = tagToName(elem->getTag());
+        tag = tags_->tagToName(elem->getTag());
         if (tag) {
             tag_len = strlen(tag);
         }
@@ -450,9 +415,7 @@
 
     bool setLast[LOG_ID_MAX];
     bool doSetLast = false;
-    log_id_for_each(i) {
-        doSetLast |= setLast[i] = mLastSet[i] && (it == mLast[i]);
-    }
+    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))
@@ -463,11 +426,11 @@
     if (doSetLast) {
         log_id_for_each(i) {
             if (setLast[i]) {
-                if (__predict_false(it == mLogElements.end())) {  // impossible
-                    mLastSet[i] = false;
-                    mLast[i] = mLogElements.begin();
+                if (__predict_false(it == mLogElements.end())) {
+                    oldest_[i] = std::nullopt;
                 } else {
-                    mLast[i] = it;  // push down the road as next-best-watermark
+                    oldest_[i] = it;  // Store the next iterator even if it does not correspond to
+                                      // the same log_id, as a starting point for GetOldest().
                 }
             }
         }
@@ -486,11 +449,6 @@
                                  b.first);
             }
         }
-        if (mLastSet[i] && (bad == mLast[i])) {
-            android::prdebug("stale mLast[%d]\n", i);
-            mLastSet[i] = false;
-            mLast[i] = mLogElements.begin();
-        }
     }
 #endif
     if (coalesce) {
@@ -576,14 +534,14 @@
 
 // If the selected reader is blocking our pruning progress, decide on
 // what kind of mitigation is necessary to unblock the situation.
-void LogBuffer::kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows) {
+void LogBuffer::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->mClient->getPid());
+                         me->client()->getPid());
         me->release_Locked();
-    } else if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
+    } else if (me->timeout().tv_sec || me->timeout().tv_nsec) {
         // Allow a blocked WRAP timeout reader to
         // trigger and start reporting the log data.
         me->triggerReader_Locked();
@@ -591,7 +549,7 @@
         // 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->mClient->getPid());
+                pruneRows, me->client()->getPid());
         me->triggerSkip_Locked(id, pruneRows);
     }
 }
@@ -644,20 +602,19 @@
 // LogBuffer::wrlock() must be held when this function is called.
 //
 bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
-    LogTimeEntry* oldest = nullptr;
+    LogReaderThread* oldest = nullptr;
     bool busy = false;
     bool clearAll = pruneRows == ULONG_MAX;
 
-    LogTimeEntry::rdlock();
+    LogReaderThread::rdlock();
 
     // Region locked?
     LastLogTimes::iterator times = mTimes.begin();
     while (times != mTimes.end()) {
-        LogTimeEntry* entry = times->get();
-        if (entry->isWatching(id) &&
-            (!oldest || (oldest->mStart > entry->mStart) ||
-             ((oldest->mStart == entry->mStart) &&
-              (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec)))) {
+        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;
         }
         times++;
@@ -668,7 +625,7 @@
     if (__predict_false(caller_uid != AID_ROOT)) {  // unlikely
         // Only here if clear all request from non system source, so chatty
         // filter logistics is not required.
-        it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+        it = GetOldest(id);
         while (it != mLogElements.end()) {
             LogBufferElement* element = *it;
 
@@ -678,12 +635,7 @@
                 continue;
             }
 
-            if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
-                mLast[id] = it;
-                mLastSet[id] = true;
-            }
-
-            if (oldest && oldest->mStart <= element->getSequence()) {
+            if (oldest && oldest->start() <= element->getSequence()) {
                 busy = true;
                 kickMe(oldest, id, pruneRows);
                 break;
@@ -694,12 +646,12 @@
                 break;
             }
         }
-        LogTimeEntry::unlock();
+        LogReaderThread::unlock();
         return busy;
     }
 
     // prune by worst offenders; by blacklist, UID, and by PID of system UID
-    bool hasBlacklist = (id != LOG_ID_SECURITY) && mPrune.naughty();
+    bool hasBlacklist = (id != LOG_ID_SECURITY) && prune_->naughty();
     while (!clearAll && (pruneRows > 0)) {
         // recalculate the worst offender on every batched pass
         int worst = -1;  // not valid for getUid() or getKey()
@@ -707,7 +659,7 @@
         size_t second_worst_sizes = 0;
         pid_t worstPid = 0;  // POSIX guarantees PID != 0
 
-        if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
+        if (worstUidEnabledForLogid(id) && prune_->worstUidEnabled()) {
             // Calculate threshold as 12.5% of available storage
             size_t threshold = log_buffer_size(id) / 8;
 
@@ -721,7 +673,7 @@
                     .findWorst(worst, worst_sizes, second_worst_sizes,
                                threshold);
 
-                if ((worst == AID_SYSTEM) && mPrune.worstPidOfSystemEnabled()) {
+                if ((worst == AID_SYSTEM) && prune_->worstPidOfSystemEnabled()) {
                     stats.sortPids(worst, (pid_t)0, 2, id)
                         .findWorst(worstPid, worst_sizes, second_worst_sizes);
                 }
@@ -734,8 +686,8 @@
         }
 
         bool kick = false;
-        bool leading = true;
-        it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+        bool leading = true;  // true if starting from the oldest log entry, false if starting from
+                              // a specific chatty entry.
         // Perform at least one mandatory garbage collection cycle in following
         // - clear leading chatty tags
         // - coalesce chatty tags
@@ -763,6 +715,9 @@
                 }
             }
         }
+        if (leading) {
+            it = GetOldest(id);
+        }
         static const timespec too_old = { EXPIRE_HOUR_THRESHOLD * 60 * 60, 0 };
         LogBufferElementCollection::iterator lastt;
         lastt = mLogElements.end();
@@ -771,7 +726,7 @@
         while (it != mLogElements.end()) {
             LogBufferElement* element = *it;
 
-            if (oldest && oldest->mStart <= element->getSequence()) {
+            if (oldest && oldest->start() <= element->getSequence()) {
                 busy = true;
                 // Do not let chatty eliding trigger any reader mitigation
                 break;
@@ -783,11 +738,6 @@
             }
             // below this point element->getLogId() == id
 
-            if (leading && (!mLastSet[id] || ((*mLast[id])->getLogId() != id))) {
-                mLast[id] = it;
-                mLastSet[id] = true;
-            }
-
             uint16_t dropped = element->getDropped();
 
             // remove any leading drops
@@ -805,7 +755,7 @@
                           ? element->getTag()
                           : element->getUid();
 
-            if (hasBlacklist && mPrune.naughty(element)) {
+            if (hasBlacklist && prune_->naughty(element)) {
                 last.clear(element);
                 it = erase(it);
                 if (dropped) {
@@ -902,14 +852,14 @@
         }
         last.clear();
 
-        if (!kick || !mPrune.worstUidEnabled()) {
+        if (!kick || !prune_->worstUidEnabled()) {
             break;  // the following loop will ask bad clients to skip/drop
         }
     }
 
     bool whitelist = false;
-    bool hasWhitelist = (id != LOG_ID_SECURITY) && mPrune.nice() && !clearAll;
-    it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+    bool hasWhitelist = (id != LOG_ID_SECURITY) && prune_->nice() && !clearAll;
+    it = GetOldest(id);
     while ((pruneRows > 0) && (it != mLogElements.end())) {
         LogBufferElement* element = *it;
 
@@ -918,18 +868,13 @@
             continue;
         }
 
-        if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
-            mLast[id] = it;
-            mLastSet[id] = true;
-        }
-
-        if (oldest && oldest->mStart <= element->getSequence()) {
+        if (oldest && oldest->start() <= element->getSequence()) {
             busy = true;
             if (!whitelist) kickMe(oldest, id, pruneRows);
             break;
         }
 
-        if (hasWhitelist && !element->getDropped() && mPrune.nice(element)) {
+        if (hasWhitelist && !element->getDropped() && prune_->nice(element)) {
             // WhiteListed
             whitelist = true;
             it++;
@@ -942,7 +887,7 @@
 
     // Do not save the whitelist if we are reader range limited
     if (whitelist && (pruneRows > 0)) {
-        it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+        it = GetOldest(id);
         while ((it != mLogElements.end()) && (pruneRows > 0)) {
             LogBufferElement* element = *it;
 
@@ -951,12 +896,7 @@
                 continue;
             }
 
-            if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
-                mLast[id] = it;
-                mLastSet[id] = true;
-            }
-
-            if (oldest && oldest->mStart <= element->getSequence()) {
+            if (oldest && oldest->start() <= element->getSequence()) {
                 busy = true;
                 kickMe(oldest, id, pruneRows);
                 break;
@@ -967,7 +907,7 @@
         }
     }
 
-    LogTimeEntry::unlock();
+    LogReaderThread::unlock();
 
     return (pruneRows > 0) && busy;
 }
@@ -990,20 +930,20 @@
             // readers and let the clear run (below) deal with determining
             // if we are still blocked and return an error code to caller.
             if (busy) {
-                LogTimeEntry::wrlock();
+                LogReaderThread::wrlock();
                 LastLogTimes::iterator times = mTimes.begin();
                 while (times != mTimes.end()) {
-                    LogTimeEntry* entry = times->get();
+                    LogReaderThread* entry = times->get();
                     // Killer punch
-                    if (entry->isWatching(id)) {
+                    if (entry->IsWatching(id)) {
                         android::prdebug(
                                 "Kicking blocked reader, pid %d, from LogBuffer::clear()\n",
-                                entry->mClient->getPid());
+                                entry->client()->getPid());
                         entry->release_Locked();
                     }
                     times++;
                 }
-                LogTimeEntry::unlock();
+                LogReaderThread::unlock();
             }
         }
         wrlock();
@@ -1047,7 +987,7 @@
 
 uint64_t LogBuffer::flushTo(SocketClient* reader, uint64_t start, pid_t* lastTid, bool privileged,
                             bool security,
-                            int (*filter)(const LogBufferElement* element, void* arg), void* arg) {
+                            const std::function<int(const LogBufferElement* element)>& filter) {
     LogBufferElementCollection::iterator it;
     uid_t uid = reader->getUid();
 
@@ -1085,7 +1025,7 @@
 
         // NB: calling out to another object with wrlock() held (safe)
         if (filter) {
-            int ret = (*filter)(element, arg);
+            int ret = filter(element);
             if (ret == false) {
                 continue;
             }
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 16225a5..3c45667 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef _LOGD_LOG_BUFFER_H__
-#define _LOGD_LOG_BUFFER_H__
+#pragma once
 
 #include <sys/types.h>
 
 #include <list>
+#include <optional>
 #include <string>
 
 #include <android/log.h>
@@ -27,51 +27,11 @@
 #include <sysutils/SocketClient.h>
 
 #include "LogBufferElement.h"
+#include "LogReaderThread.h"
 #include "LogStatistics.h"
 #include "LogTags.h"
-#include "LogTimes.h"
 #include "LogWhiteBlackList.h"
 
-//
-// We are either in 1970ish (MONOTONIC) or 2016+ish (REALTIME) so to
-// differentiate without prejudice, we use 1972 to delineate, earlier
-// is likely monotonic, later is real. Otherwise we start using a
-// dividing line between monotonic and realtime if more than a minute
-// difference between them.
-//
-namespace android {
-
-static bool isMonotonic(const log_time& mono) {
-    static const uint32_t EPOCH_PLUS_2_YEARS = 2 * 24 * 60 * 60 * 1461 / 4;
-    static const uint32_t EPOCH_PLUS_MINUTE = 60;
-
-    if (mono.tv_sec >= EPOCH_PLUS_2_YEARS) {
-        return false;
-    }
-
-    log_time now(CLOCK_REALTIME);
-
-    /* Timezone and ntp time setup? */
-    if (now.tv_sec >= EPOCH_PLUS_2_YEARS) {
-        return true;
-    }
-
-    /* no way to differentiate realtime from monotonic time */
-    if (now.tv_sec < EPOCH_PLUS_MINUTE) {
-        return false;
-    }
-
-    log_time cpu(CLOCK_MONOTONIC);
-    /* too close to call to differentiate monotonic times from realtime */
-    if ((cpu.tv_sec + EPOCH_PLUS_MINUTE) >= now.tv_sec) {
-        return false;
-    }
-
-    /* dividing line half way between monotonic and realtime */
-    return mono.tv_sec < ((cpu.tv_sec + now.tv_sec) / 2);
-}
-}
-
 typedef std::list<LogBufferElement*> LogBufferElementCollection;
 
 class LogBuffer {
@@ -80,10 +40,6 @@
 
     LogStatistics stats;
 
-    PruneList mPrune;
-    // watermark for last per log id
-    LogBufferElementCollection::iterator mLast[LOG_ID_MAX];
-    bool mLastSet[LOG_ID_MAX];
     // watermark of any worst/chatty uid processing
     typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator>
         LogBufferIteratorMap;
@@ -95,10 +51,6 @@
 
     unsigned long mMaxSize[LOG_ID_MAX];
 
-    bool monotonic;
-
-    LogTags tags;
-
     LogBufferElement* lastLoggedElements[LOG_ID_MAX];
     LogBufferElement* droppedElements[LOG_ID_MAX];
     void log(LogBufferElement* elem);
@@ -106,12 +58,9 @@
    public:
     LastLogTimes& mTimes;
 
-    explicit LogBuffer(LastLogTimes* times);
+    LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune);
     ~LogBuffer();
     void init();
-    bool isMonotonic() {
-        return monotonic;
-    }
 
     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);
@@ -121,8 +70,7 @@
     uint64_t flushTo(SocketClient* writer, uint64_t start,
                      pid_t* lastTid,  // &lastTid[LOG_ID_MAX] or nullptr
                      bool privileged, bool security,
-                     int (*filter)(const LogBufferElement* element, void* arg) = nullptr,
-                     void* arg = nullptr);
+                     const std::function<int(const LogBufferElement* element)>& filter);
 
     bool clear(log_id_t id, uid_t uid = AID_ROOT);
     unsigned long getSize(log_id_t id);
@@ -135,24 +83,6 @@
         stats.enableStatistics();
     }
 
-    int initPrune(const char* cp) {
-        return mPrune.init(cp);
-    }
-    std::string formatPrune() {
-        return mPrune.format();
-    }
-
-    std::string formatGetEventTag(uid_t uid, const char* name,
-                                  const char* format) {
-        return tags.formatGetEventTag(uid, name, format);
-    }
-    std::string formatEntry(uint32_t tag, uid_t uid) {
-        return tags.formatEntry(tag, uid);
-    }
-    const char* tagToName(uint32_t tag) {
-        return tags.tagToName(tag);
-    }
-
     // helper must be protected directly or implicitly by wrlock()/unlock()
     const char* pidToName(pid_t pid) {
         return stats.pidToName(pid);
@@ -176,11 +106,20 @@
     static constexpr size_t maxPrune = 256;
 
     void maybePrune(log_id_t id);
-    void kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows);
+    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);
-};
 
-#endif  // _LOGD_LOG_BUFFER_H__
+    // 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_;
+
+    // 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/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 3714800..916ed42 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -22,6 +22,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <log/log_read.h>
 #include <private/android_logger.h>
 
 #include "LogBuffer.h"
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index edd326a..c308073 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -309,8 +309,6 @@
         }
         buf = cp;
 
-        if (isMonotonic()) return now;
-
         const char* b;
         if (((b = android::strnstr(cp, len, suspendStr))) &&
             (((b += strlen(suspendStr)) - cp) < len)) {
@@ -356,11 +354,7 @@
 
         convertMonotonicToReal(now);
     } else {
-        if (isMonotonic()) {
-            now = log_time(CLOCK_MONOTONIC);
-        } else {
-            now = log_time(CLOCK_REALTIME);
-        }
+        now = log_time(CLOCK_REALTIME);
     }
     return now;
 }
@@ -431,45 +425,6 @@
     return pri;
 }
 
-// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
-// compensated start time.
-void LogKlog::synchronize(const char* buf, ssize_t len) {
-    const char* cp = android::strnstr(buf, len, suspendStr);
-    if (!cp) {
-        cp = android::strnstr(buf, len, resumeStr);
-        if (!cp) return;
-    } else {
-        const char* rp = android::strnstr(buf, len, resumeStr);
-        if (rp && (rp < cp)) cp = rp;
-    }
-
-    do {
-        --cp;
-    } while ((cp > buf) && (*cp != '\n'));
-    if (*cp == '\n') {
-        ++cp;
-    }
-    parseKernelPrio(cp, len - (cp - buf));
-
-    log_time now = sniffTime(cp, len - (cp - buf), true);
-
-    const char* suspended = android::strnstr(buf, len, suspendedStr);
-    if (!suspended || (suspended > cp)) {
-        return;
-    }
-    cp = suspended;
-
-    do {
-        --cp;
-    } while ((cp > buf) && (*cp != '\n'));
-    if (*cp == '\n') {
-        ++cp;
-    }
-    parseKernelPrio(cp, len - (cp - buf));
-
-    sniffTime(cp, len - (cp - buf), true);
-}
-
 // Convert kernel log priority number into an Android Logger priority number
 static int convertKernelPrioToAndroidPrio(int pri) {
     switch (pri & LOG_PRIMASK) {
@@ -789,7 +744,7 @@
     memcpy(np, p, b);
     np[b] = '\0';
 
-    if (!isMonotonic()) {
+    {
         // Watch out for singular race conditions with timezone causing near
         // integer quarter-hour jumps in the time and compensate accordingly.
         // Entries will be temporal within near_seconds * 2. b/21868540
@@ -819,7 +774,7 @@
 
     // notify readers
     if (rc > 0) {
-        reader->notifyNewLog(static_cast<log_mask_t>(1 << LOG_ID_KERNEL));
+        reader->notifyNewLog(static_cast<unsigned int>(1 << LOG_ID_KERNEL));
     }
 
     return rc;
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index 6bfd6a8..4c09751 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -42,17 +42,10 @@
     LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
             bool auditd);
     int log(const char* buf, ssize_t len);
-    void synchronize(const char* buf, ssize_t len);
 
-    bool isMonotonic() {
-        return logbuf->isMonotonic();
-    }
     static void convertMonotonicToReal(log_time& real) {
         real += correction;
     }
-    static void convertRealToMonotonic(log_time& real) {
-        real -= correction;
-    }
 
    protected:
      log_time sniffTime(const char*& buf, ssize_t len, bool reverse);
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index ba61042..fbe6ea0 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -22,6 +22,8 @@
 #include <sys/un.h>
 #include <unistd.h>
 
+#include <thread>
+
 #include <cutils/sockets.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
@@ -31,33 +33,47 @@
 #include "LogUtils.h"
 
 LogListener::LogListener(LogBuffer* buf, LogReader* reader)
-    : SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {}
+    : socket_(GetLogSocket()), logbuf_(buf), reader_(reader) {}
 
-bool LogListener::onDataAvailable(SocketClient* cli) {
+bool LogListener::StartListener() {
+    if (socket_ <= 0) {
+        return false;
+    }
+    auto thread = std::thread(&LogListener::ThreadFunction, this);
+    thread.detach();
+    return true;
+}
+
+void LogListener::ThreadFunction() {
     static bool name_set;
     if (!name_set) {
         prctl(PR_SET_NAME, "logd.writer");
         name_set = true;
     }
 
+    while (true) {
+        HandleData();
+    }
+}
+
+void LogListener::HandleData() {
     // + 1 to ensure null terminator if MAX_PAYLOAD buffer is received
-    char buffer[sizeof(android_log_header_t) + LOGGER_ENTRY_MAX_PAYLOAD + 1];
-    struct iovec iov = { buffer, sizeof(buffer) - 1 };
+    __attribute__((uninitialized)) char
+            buffer[sizeof(android_log_header_t) + LOGGER_ENTRY_MAX_PAYLOAD + 1];
+    struct iovec iov = {buffer, sizeof(buffer) - 1};
 
     alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
     struct msghdr hdr = {
         nullptr, 0, &iov, 1, control, sizeof(control), 0,
     };
 
-    int socket = cli->getSocket();
-
     // To clear the entire buffer is secure/safe, but this contributes to 1.68%
     // overhead under logging load. We are safe because we check counts, but
     // still need to clear null terminator
     // memset(buffer, 0, sizeof(buffer));
-    ssize_t n = recvmsg(socket, &hdr, 0);
+    ssize_t n = recvmsg(socket_, &hdr, 0);
     if (n <= (ssize_t)(sizeof(android_log_header_t))) {
-        return false;
+        return;
     }
 
     buffer[n] = 0;
@@ -75,14 +91,14 @@
     }
 
     if (cred == nullptr) {
-        return false;
+        return;
     }
 
     if (cred->uid == AID_LOGD) {
         // ignore log messages we send to ourself.
         // Such log messages are often generated by libraries we depend on
         // which use standard Android logging.
-        return false;
+        return;
     }
 
     android_log_header_t* header =
@@ -90,13 +106,13 @@
     log_id_t logId = static_cast<log_id_t>(header->id);
     if (/* logId < LOG_ID_MIN || */ logId >= LOG_ID_MAX ||
         logId == LOG_ID_KERNEL) {
-        return false;
+        return;
     }
 
     if ((logId == LOG_ID_SECURITY) &&
         (!__android_log_security() ||
          !clientHasLogCredentials(cred->uid, cred->gid, cred->pid))) {
-        return false;
+        return;
     }
 
     char* msg = ((char*)buffer) + sizeof(android_log_header_t);
@@ -105,16 +121,16 @@
     // 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);
+    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<log_mask_t>(1 << logId));
+        reader_->notifyNewLog(static_cast<unsigned int>(1 << logId));
     }
 
-    return true;
+    return;
 }
 
-int LogListener::getLogSocket() {
+int LogListener::GetLogSocket() {
     static const char socketName[] = "logdw";
     int sock = android_get_control_socket(socketName);
 
diff --git a/logd/LogListener.h b/logd/LogListener.h
index 8fe3da4..ce3e0f2 100644
--- a/logd/LogListener.h
+++ b/logd/LogListener.h
@@ -14,24 +14,22 @@
  * limitations under the License.
  */
 
-#ifndef _LOGD_LOG_LISTENER_H__
-#define _LOGD_LOG_LISTENER_H__
+#pragma once
 
-#include <sysutils/SocketListener.h>
+#include "LogBuffer.h"
 #include "LogReader.h"
 
-class LogListener : public SocketListener {
-    LogBuffer* logbuf;
-    LogReader* reader;
+class LogListener {
+  public:
+    LogListener(LogBuffer* buf, LogReader* reader);
+    bool StartListener();
 
-   public:
-     LogListener(LogBuffer* buf, LogReader* reader);
+  private:
+    void ThreadFunction();
+    void HandleData();
+    static int GetLogSocket();
 
-   protected:
-    virtual bool onDataAvailable(SocketClient* cli);
-
-   private:
-    static int getLogSocket();
+    int socket_;
+    LogBuffer* logbuf_;
+    LogReader* reader_;
 };
-
-#endif
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index f79d39c..a590cef 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -24,21 +24,35 @@
 #include <cutils/sockets.h>
 #include <private/android_logger.h>
 
-#include "FlushCommand.h"
 #include "LogBuffer.h"
 #include "LogBufferElement.h"
 #include "LogReader.h"
 #include "LogUtils.h"
 
+static bool CanReadSecurityLogs(SocketClient* client) {
+    return client->getUid() == AID_SYSTEM || client->getGid() == AID_SYSTEM;
+}
+
 LogReader::LogReader(LogBuffer* logbuf)
     : SocketListener(getLogSocket(), true), mLogbuf(*logbuf) {
 }
 
 // When we are notified a new log entry is available, inform
 // listening sockets who are watching this entry's log id.
-void LogReader::notifyNewLog(log_mask_t logMask) {
-    FlushCommand command(*this, logMask);
-    runOnEachSocket(&command);
+void LogReader::notifyNewLog(unsigned int log_mask) {
+    LastLogTimes& times = mLogbuf.mTimes;
+
+    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();
+    }
+    LogReaderThread::unlock();
 }
 
 // Note returning false will release the SocketClient instance.
@@ -60,15 +74,15 @@
 
     // Clients are only allowed to send one command, disconnect them if they
     // send another.
-    LogTimeEntry::wrlock();
+    LogReaderThread::wrlock();
     for (const auto& entry : mLogbuf.mTimes) {
-        if (entry->mClient == cli) {
+        if (entry->client() == cli) {
             entry->release_Locked();
-            LogTimeEntry::unlock();
+            LogReaderThread::unlock();
             return false;
         }
     }
-    LogTimeEntry::unlock();
+    LogReaderThread::unlock();
 
     unsigned long tail = 0;
     static const char _tail[] = " tail=";
@@ -123,66 +137,46 @@
     if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) {
         // Allow writer to get some cycles, and wait for pending notifications
         sched_yield();
-        LogTimeEntry::wrlock();
-        LogTimeEntry::unlock();
+        LogReaderThread::wrlock();
+        LogReaderThread::unlock();
         sched_yield();
         nonBlock = true;
     }
 
+    bool privileged = clientHasLogCredentials(cli);
+    bool can_read_security = CanReadSecurityLogs(cli);
+
     uint64_t sequence = 1;
     // Convert realtime to sequence number
     if (start != log_time::EPOCH) {
-        class LogFindStart {
-            const pid_t mPid;
-            const unsigned mLogMask;
-            bool startTimeSet;
-            const log_time start;
-            uint64_t& sequence;
-            uint64_t last;
-            bool isMonotonic;
-
-          public:
-            LogFindStart(unsigned logMask, pid_t pid, log_time start, uint64_t& sequence,
-                         bool isMonotonic)
-                : mPid(pid),
-                  mLogMask(logMask),
-                  startTimeSet(false),
-                  start(start),
-                  sequence(sequence),
-                  last(sequence),
-                  isMonotonic(isMonotonic) {}
-
-            static int callback(const LogBufferElement* element, void* obj) {
-                LogFindStart* me = reinterpret_cast<LogFindStart*>(obj);
-                if ((!me->mPid || (me->mPid == element->getPid())) &&
-                    (me->mLogMask & (1 << element->getLogId()))) {
-                    if (me->start == element->getRealTime()) {
-                        me->sequence = element->getSequence();
-                        me->startTimeSet = true;
-                        return -1;
-                    } else if (!me->isMonotonic || android::isMonotonic(element->getRealTime())) {
-                        if (me->start < element->getRealTime()) {
-                            me->sequence = me->last;
-                            me->startTimeSet = true;
-                            return -1;
-                        }
-                        me->last = element->getSequence();
-                    } else {
-                        me->last = element->getSequence();
-                    }
-                }
-                return false;
+        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 {
+            if (pid && pid != element->getPid()) {
+                return 0;
             }
+            if ((logMask & (1 << element->getLogId())) == 0) {
+                return 0;
+            }
+            if (start == element->getRealTime()) {
+                sequence = element->getSequence();
+                start_time_set = true;
+                return -1;
+            } else {
+                if (start < element->getRealTime()) {
+                    sequence = last;
+                    start_time_set = true;
+                    return -1;
+                }
+                last = element->getSequence();
+            }
+            return 0;
+        };
 
-            bool found() { return startTimeSet; }
-        } logFindStart(logMask, pid, start, sequence,
-                       logbuf().isMonotonic() && android::isMonotonic(start));
+        logbuf().flushTo(cli, sequence, nullptr, privileged, can_read_security, log_find_start);
 
-        logbuf().flushTo(cli, sequence, nullptr, FlushCommand::hasReadLogs(cli),
-                         FlushCommand::hasSecurityLogs(cli),
-                         logFindStart.callback, &logFindStart);
-
-        if (!logFindStart.found()) {
+        if (!start_time_set) {
             if (nonBlock) {
                 doSocketDelete(cli);
                 return false;
@@ -201,11 +195,12 @@
         timeout = 0;
     }
 
-    LogTimeEntry::wrlock();
-    auto entry = std::make_unique<LogTimeEntry>(*this, cli, nonBlock, tail, logMask, pid, start,
-                                                sequence, timeout);
+    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()) {
-        LogTimeEntry::unlock();
+        LogReaderThread::unlock();
         return false;
     }
 
@@ -218,24 +213,24 @@
     setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char*)&t,
                sizeof(t));
 
-    LogTimeEntry::unlock();
+    LogReaderThread::unlock();
 
     return true;
 }
 
 void LogReader::doSocketDelete(SocketClient* cli) {
     LastLogTimes& times = mLogbuf.mTimes;
-    LogTimeEntry::wrlock();
+    LogReaderThread::wrlock();
     LastLogTimes::iterator it = times.begin();
     while (it != times.end()) {
-        LogTimeEntry* entry = it->get();
-        if (entry->mClient == cli) {
+        LogReaderThread* entry = it->get();
+        if (entry->client() == cli) {
             entry->release_Locked();
             break;
         }
         it++;
     }
-    LogTimeEntry::unlock();
+    LogReaderThread::unlock();
 }
 
 int LogReader::getLogSocket() {
diff --git a/logd/LogReader.h b/logd/LogReader.h
index b5312b6..f00cc21 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef _LOGD_LOG_WRITER_H__
-#define _LOGD_LOG_WRITER_H__
+#pragma once
 
 #include <sysutils/SocketListener.h>
 
-#include "LogTimes.h"
+#include "LogReaderThread.h"
 
 #define LOGD_SNDTIMEO 32
 
@@ -30,7 +29,7 @@
 
    public:
     explicit LogReader(LogBuffer* logbuf);
-    void notifyNewLog(log_mask_t logMask);
+    void notifyNewLog(unsigned int logMask);
 
     LogBuffer& logbuf(void) const {
         return mLogbuf;
@@ -44,5 +43,3 @@
 
     void doSocketDelete(SocketClient* cli);
 };
-
-#endif
diff --git a/logd/LogReaderThread.cpp b/logd/LogReaderThread.cpp
new file mode 100644
index 0000000..5413c4d
--- /dev/null
+++ b/logd/LogReaderThread.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "LogReaderThread.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/prctl.h>
+
+#include <thread>
+
+#include "LogBuffer.h"
+#include "LogReader.h"
+
+using namespace std::placeholders;
+
+pthread_mutex_t LogReaderThread::timesLock = PTHREAD_MUTEX_INITIALIZER;
+
+LogReaderThread::LogReaderThread(LogReader& reader, SocketClient* client, 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_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;
+    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();
+
+    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 (release_) {
+                break;
+            }
+        }
+
+        unlock();
+
+        if (tail_) {
+            logbuf.flushTo(client, start, nullptr, privileged_, can_read_security_logs_,
+                           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));
+
+        // 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
+        // wouldn't have seen them.
+        // Note: this is still racy and may skip out of order events that came in since the last
+        // time the client disconnected and then reconnected with the new start time.  The long term
+        // solution here is that clients must request events since a specific sequence number.
+        start_time_.tv_sec = 0;
+        start_time_.tv_nsec = 0;
+
+        wrlock();
+
+        if (start == LogBufferElement::FLUSH_ERROR) {
+            break;
+        }
+
+        start_ = start + 1;
+
+        if (non_block_ || release_) {
+            break;
+        }
+
+        cleanSkip_Locked();
+
+        if (!timeout_.tv_sec && !timeout_.tv_nsec) {
+            pthread_cond_wait(&thread_triggered_condition_, &timesLock);
+        }
+    }
+
+    LogReader& reader = reader_;
+    reader.release(client);
+
+    client->decRef();
+
+    LastLogTimes& times = reader.logbuf().mTimes;
+    auto it = std::find_if(times.begin(), times.end(),
+                           [this](const auto& other) { return other.get() == this; });
+
+    if (it != times.end()) {
+        times.erase(it);
+    }
+
+    unlock();
+}
+
+// A first pass to count the number of elements
+int LogReaderThread::FilterFirstPass(const LogBufferElement* element) {
+    LogReaderThread::wrlock();
+
+    if (leading_dropped_) {
+        if (element->getDropped()) {
+            LogReaderThread::unlock();
+            return false;
+        }
+        leading_dropped_ = false;
+    }
+
+    if (count_ == 0) {
+        start_ = element->getSequence();
+    }
+
+    if ((!pid_ || pid_ == element->getPid()) && IsWatching(element->getLogId()) &&
+        (start_time_ == log_time::EPOCH || start_time_ <= element->getRealTime())) {
+        ++count_;
+    }
+
+    LogReaderThread::unlock();
+
+    return false;
+}
+
+// A second pass to send the selected elements
+int LogReaderThread::FilterSecondPass(const LogBufferElement* element) {
+    LogReaderThread::wrlock();
+
+    start_ = element->getSequence();
+
+    if (skip_ahead_[element->getLogId()]) {
+        skip_ahead_[element->getLogId()]--;
+        goto skip;
+    }
+
+    if (leading_dropped_) {
+        if (element->getDropped()) {
+            goto skip;
+        }
+        leading_dropped_ = false;
+    }
+
+    // Truncate to close race between first and second pass
+    if (non_block_ && tail_ && index_ >= count_) {
+        goto stop;
+    }
+
+    if (!IsWatching(element->getLogId())) {
+        goto skip;
+    }
+
+    if (pid_ && pid_ != element->getPid()) {
+        goto skip;
+    }
+
+    if (start_time_ != log_time::EPOCH && element->getRealTime() <= start_time_) {
+        goto skip;
+    }
+
+    if (release_) {
+        goto stop;
+    }
+
+    if (!tail_) {
+        goto ok;
+    }
+
+    ++index_;
+
+    if (count_ > tail_ && index_ <= (count_ - tail_)) {
+        goto skip;
+    }
+
+    if (!non_block_) {
+        tail_ = 0;
+    }
+
+ok:
+    if (!skip_ahead_[element->getLogId()]) {
+        LogReaderThread::unlock();
+        return true;
+    }
+    // FALLTHRU
+
+skip:
+    LogReaderThread::unlock();
+    return false;
+
+stop:
+    LogReaderThread::unlock();
+    return -1;
+}
+
+void LogReaderThread::cleanSkip_Locked(void) {
+    memset(skip_ahead_, 0, sizeof(skip_ahead_));
+}
diff --git a/logd/LogReaderThread.h b/logd/LogReaderThread.h
new file mode 100644
index 0000000..39a8b63
--- /dev/null
+++ b/logd/LogReaderThread.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2012-2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <pthread.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <list>
+#include <memory>
+
+#include <log/log.h>
+#include <sysutils/SocketClient.h>
+
+class LogReader;
+class LogBufferElement;
+
+class LogReaderThread {
+    static pthread_mutex_t timesLock;
+
+  public:
+    LogReaderThread(LogReader& reader, SocketClient* client, 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_); }
+
+    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);
+        release_ = true;
+        pthread_cond_signal(&thread_triggered_condition_);
+    }
+
+    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_; }
+    uint64_t start() const { return start_; }
+    const timespec& timeout() const { return timeout_; }
+
+  private:
+    void ThreadFunction();
+    // flushTo filter callbacks
+    int FilterFirstPass(const LogBufferElement* element);
+    int FilterSecondPass(const LogBufferElement* element);
+
+    // 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.
+    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.
+    const pid_t pid_;
+    // When a reader is referencing (via start_) old elements in the log buffer, and the log
+    // buffer's size grows past its memory limit, the log buffer may request the reader to skip
+    // ahead a specified number of logs.
+    unsigned int skip_ahead_[LOG_ID_MAX];
+    // Used for distinguishing 'dropped' messages for duplicate logs vs chatty drops
+    pid_t last_tid_[LOG_ID_MAX];
+
+    // These next three variables are used for reading only the most recent lines aka `adb logcat
+    // -t` / `adb logcat -T`.
+    // tail_ is the number of most recent lines to print.
+    unsigned long tail_;
+    // count_ is the result of a first pass through the log buffer to determine how many total
+    // messages there are.
+    unsigned long count_;
+    // index_ is used along with count_ to only start sending lines once index_ > (count_ - tail_)
+    // 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
+    // wrap, then wake up and send the logs to the reader anyway.
+    timespec timeout_;
+    // 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/LogTags.cpp b/logd/LogTags.cpp
index 8299e66..082e4a1 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -34,6 +34,7 @@
 #include <android-base/stringprintf.h>
 #include <log/log_event_list.h>
 #include <log/log_properties.h>
+#include <log/log_read.h>
 #include <private/android_filesystem_config.h>
 
 #include "LogTags.h"
@@ -98,8 +99,7 @@
             struct tm tm;
             localtime_r(&now, &tm);
             char timebuf[20];
-            size_t len =
-                strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", &tm);
+            strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", &tm);
             android::base::WriteStringToFd(
                 android::base::StringPrintf(
                     "# Rebuilt %.20s, content owned by logd\n", timebuf),
@@ -188,7 +188,6 @@
 // Read the event log tags file, and build up our internal database
 void LogTags::ReadFileEventLogTags(const char* filename, bool warn) {
     bool etc = !strcmp(filename, system_event_log_tags);
-    bool debug = !etc && !strcmp(filename, debug_event_log_tags);
 
     if (!etc) {
         RebuildFileEventLogTags(filename, warn);
@@ -390,23 +389,6 @@
     return me->tagToName(tag);
 }
 
-// Prototype in LogUtils.h allowing external access to our database.
-//
-// This only works on userdebug and eng devices to re-read the
-// /data/misc/logd/event-log-tags file right after /data is mounted.
-// The operation is near to boot and should only happen once.  There
-// are races associated with its use since it can trigger a Rebuild
-// of the file, but that is a can-not-happen since the file was not
-// read yet.  More dangerous if called later, but if all is well it
-// should just skip over everything and not write any new entries.
-void android::ReReadEventLogTags() {
-    LogTags* me = logtags;
-
-    if (me && __android_log_is_debuggable()) {
-        me->ReadFileEventLogTags(me->debug_event_log_tags);
-    }
-}
-
 // converts an event tag into a format
 const char* LogTags::tagToFormat(uint32_t tag) const {
     tag2format_const_iterator iform;
@@ -564,7 +546,7 @@
      */
 
     struct timespec ts;
-    clock_gettime(android_log_clockid(), &ts);
+    clock_gettime(CLOCK_REALTIME, &ts);
 
     android_log_header_t header = {
         .id = LOG_ID_EVENTS,
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
deleted file mode 100644
index ed8d2f5..0000000
--- a/logd/LogTimes.cpp
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#include <errno.h>
-#include <string.h>
-#include <sys/prctl.h>
-
-#include "FlushCommand.h"
-#include "LogBuffer.h"
-#include "LogReader.h"
-#include "LogTimes.h"
-
-pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER;
-
-LogTimeEntry::LogTimeEntry(LogReader& reader, SocketClient* client, bool nonBlock,
-                           unsigned long tail, log_mask_t logMask, pid_t pid, log_time start_time,
-                           uint64_t start, uint64_t timeout)
-    : leadingDropped(false),
-      mReader(reader),
-      mLogMask(logMask),
-      mPid(pid),
-      mCount(0),
-      mTail(tail),
-      mIndex(0),
-      mClient(client),
-      mStartTime(start_time),
-      mStart(start),
-      mNonBlock(nonBlock) {
-    mTimeout.tv_sec = timeout / NS_PER_SEC;
-    mTimeout.tv_nsec = timeout % NS_PER_SEC;
-    memset(mLastTid, 0, sizeof(mLastTid));
-    pthread_cond_init(&threadTriggeredCondition, nullptr);
-    cleanSkip_Locked();
-}
-
-bool LogTimeEntry::startReader_Locked() {
-    pthread_attr_t attr;
-
-    if (!pthread_attr_init(&attr)) {
-        if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
-            if (!pthread_create(&mThread, &attr, LogTimeEntry::threadStart,
-                                this)) {
-                pthread_attr_destroy(&attr);
-                return true;
-            }
-        }
-        pthread_attr_destroy(&attr);
-    }
-
-    return false;
-}
-
-void* LogTimeEntry::threadStart(void* obj) {
-    prctl(PR_SET_NAME, "logd.reader.per");
-
-    LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
-    SocketClient* client = me->mClient;
-
-    LogBuffer& logbuf = me->mReader.logbuf();
-
-    bool privileged = FlushCommand::hasReadLogs(client);
-    bool security = FlushCommand::hasSecurityLogs(client);
-
-    me->leadingDropped = true;
-
-    wrlock();
-
-    uint64_t start = me->mStart;
-
-    while (!me->mRelease) {
-        if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
-            if (pthread_cond_clockwait(&me->threadTriggeredCondition, &timesLock, CLOCK_MONOTONIC,
-                                       &me->mTimeout) == ETIMEDOUT) {
-                me->mTimeout.tv_sec = 0;
-                me->mTimeout.tv_nsec = 0;
-            }
-            if (me->mRelease) {
-                break;
-            }
-        }
-
-        unlock();
-
-        if (me->mTail) {
-            logbuf.flushTo(client, start, nullptr, privileged, security,
-                           FilterFirstPass, me);
-            me->leadingDropped = true;
-        }
-        start = logbuf.flushTo(client, start, me->mLastTid, privileged,
-                               security, FilterSecondPass, me);
-
-        // 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
-        // wouldn't have seen them.
-        // Note: this is still racy and may skip out of order events that came in since the last
-        // time the client disconnected and then reconnected with the new start time.  The long term
-        // solution here is that clients must request events since a specific sequence number.
-        me->mStartTime.tv_sec = 0;
-        me->mStartTime.tv_nsec = 0;
-
-        wrlock();
-
-        if (start == LogBufferElement::FLUSH_ERROR) {
-            break;
-        }
-
-        me->mStart = start + 1;
-
-        if (me->mNonBlock || me->mRelease) {
-            break;
-        }
-
-        me->cleanSkip_Locked();
-
-        if (!me->mTimeout.tv_sec && !me->mTimeout.tv_nsec) {
-            pthread_cond_wait(&me->threadTriggeredCondition, &timesLock);
-        }
-    }
-
-    LogReader& reader = me->mReader;
-    reader.release(client);
-
-    client->decRef();
-
-    LastLogTimes& times = reader.logbuf().mTimes;
-    auto it =
-        std::find_if(times.begin(), times.end(),
-                     [&me](const auto& other) { return other.get() == me; });
-
-    if (it != times.end()) {
-        times.erase(it);
-    }
-
-    unlock();
-
-    return nullptr;
-}
-
-// A first pass to count the number of elements
-int LogTimeEntry::FilterFirstPass(const LogBufferElement* element, void* obj) {
-    LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
-    LogTimeEntry::wrlock();
-
-    if (me->leadingDropped) {
-        if (element->getDropped()) {
-            LogTimeEntry::unlock();
-            return false;
-        }
-        me->leadingDropped = false;
-    }
-
-    if (me->mCount == 0) {
-        me->mStart = element->getSequence();
-    }
-
-    if ((!me->mPid || me->mPid == element->getPid()) && me->isWatching(element->getLogId()) &&
-        (me->mStartTime == log_time::EPOCH || me->mStartTime <= element->getRealTime())) {
-        ++me->mCount;
-    }
-
-    LogTimeEntry::unlock();
-
-    return false;
-}
-
-// A second pass to send the selected elements
-int LogTimeEntry::FilterSecondPass(const LogBufferElement* element, void* obj) {
-    LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
-    LogTimeEntry::wrlock();
-
-    me->mStart = element->getSequence();
-
-    if (me->skipAhead[element->getLogId()]) {
-        me->skipAhead[element->getLogId()]--;
-        goto skip;
-    }
-
-    if (me->leadingDropped) {
-        if (element->getDropped()) {
-            goto skip;
-        }
-        me->leadingDropped = false;
-    }
-
-    // Truncate to close race between first and second pass
-    if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) {
-        goto stop;
-    }
-
-    if (!me->isWatching(element->getLogId())) {
-        goto skip;
-    }
-
-    if (me->mPid && (me->mPid != element->getPid())) {
-        goto skip;
-    }
-
-    if (me->mStartTime != log_time::EPOCH && element->getRealTime() <= me->mStartTime) {
-        goto skip;
-    }
-
-    if (me->mRelease) {
-        goto stop;
-    }
-
-    if (!me->mTail) {
-        goto ok;
-    }
-
-    ++me->mIndex;
-
-    if ((me->mCount > me->mTail) && (me->mIndex <= (me->mCount - me->mTail))) {
-        goto skip;
-    }
-
-    if (!me->mNonBlock) {
-        me->mTail = 0;
-    }
-
-ok:
-    if (!me->skipAhead[element->getLogId()]) {
-        LogTimeEntry::unlock();
-        return true;
-    }
-// FALLTHRU
-
-skip:
-    LogTimeEntry::unlock();
-    return false;
-
-stop:
-    LogTimeEntry::unlock();
-    return -1;
-}
-
-void LogTimeEntry::cleanSkip_Locked(void) {
-    memset(skipAhead, 0, sizeof(skipAhead));
-}
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
deleted file mode 100644
index a99c73b..0000000
--- a/logd/LogTimes.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2012-2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LOGD_LOG_TIMES_H__
-#define _LOGD_LOG_TIMES_H__
-
-#include <pthread.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <time.h>
-
-#include <list>
-#include <memory>
-
-#include <log/log.h>
-#include <sysutils/SocketClient.h>
-
-typedef unsigned int log_mask_t;
-
-class LogReader;
-class LogBufferElement;
-
-class LogTimeEntry {
-    static pthread_mutex_t timesLock;
-    bool mRelease = false;
-    bool leadingDropped;
-    pthread_cond_t threadTriggeredCondition;
-    pthread_t mThread;
-    LogReader& mReader;
-    static void* threadStart(void* me);
-    const log_mask_t mLogMask;
-    const pid_t mPid;
-    unsigned int skipAhead[LOG_ID_MAX];
-    pid_t mLastTid[LOG_ID_MAX];
-    unsigned long mCount;
-    unsigned long mTail;
-    unsigned long mIndex;
-
-  public:
-    LogTimeEntry(LogReader& reader, SocketClient* client, bool nonBlock, unsigned long tail,
-                 log_mask_t logMask, pid_t pid, log_time start_time, uint64_t sequence,
-                 uint64_t timeout);
-
-    SocketClient* mClient;
-    log_time mStartTime;
-    uint64_t mStart;
-    struct timespec mTimeout;  // CLOCK_MONOTONIC based timeout used for log wrapping.
-    const bool mNonBlock;
-
-    // Protect List manipulations
-    static void wrlock(void) {
-        pthread_mutex_lock(&timesLock);
-    }
-    static void rdlock(void) {
-        pthread_mutex_lock(&timesLock);
-    }
-    static void unlock(void) {
-        pthread_mutex_unlock(&timesLock);
-    }
-
-    bool startReader_Locked();
-
-    void triggerReader_Locked(void) {
-        pthread_cond_signal(&threadTriggeredCondition);
-    }
-
-    void triggerSkip_Locked(log_id_t id, unsigned int skip) {
-        skipAhead[id] = skip;
-    }
-    void cleanSkip_Locked(void);
-
-    void release_Locked(void) {
-        // gracefully shut down the socket.
-        shutdown(mClient->getSocket(), SHUT_RDWR);
-        mRelease = true;
-        pthread_cond_signal(&threadTriggeredCondition);
-    }
-
-    bool isWatching(log_id_t id) const {
-        return mLogMask & (1 << id);
-    }
-    bool isWatchingMultiple(log_mask_t logMask) const {
-        return mLogMask & logMask;
-    }
-    // flushTo filter callbacks
-    static int FilterFirstPass(const LogBufferElement* element, void* me);
-    static int FilterSecondPass(const LogBufferElement* element, void* me);
-};
-
-typedef std::list<std::unique_ptr<LogTimeEntry>> LastLogTimes;
-
-#endif  // _LOGD_LOG_TIMES_H__
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index fa9f398..f9cd42d 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LOGD_LOG_UTILS_H__
-#define _LOGD_LOG_UTILS_H__
+#pragma once
 
 #include <sys/cdefs.h>
 #include <sys/types.h>
@@ -41,7 +40,6 @@
 
 // Furnished in LogTags.cpp. Thread safe.
 const char* tagToName(uint32_t tag);
-void ReReadEventLogTags();
 
 // Furnished by LogKlog.cpp
 char* log_strntok_r(char* s, ssize_t& len, char*& saveptr, ssize_t& sublen);
@@ -72,5 +70,3 @@
     return (id == LOG_ID_MAIN) || (id == LOG_ID_SYSTEM) ||
            (id == LOG_ID_RADIO) || (id == LOG_ID_EVENTS);
 }
-
-#endif  // _LOGD_LOG_UTILS_H__
diff --git a/logd/README.property b/logd/README.property
index 1b7e165..6a9369a 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -44,10 +44,6 @@
                                          oldest entries of chattiest UID, and
                                          the chattiest PID of system
                                          (1000, or AID_SYSTEM).
-persist.logd.timestamp     string  ro    The recording timestamp source.
-                                         "m[onotonic]" is the only supported
-                                         key character, otherwise realtime.
-ro.logd.timestamp        string realtime default for persist.logd.timestamp
 log.tag                   string persist The global logging level, VERBOSE,
                                          DEBUG, INFO, WARN, ERROR, ASSERT or
                                          SILENT. Only the first character is
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index 4d1589b..8156612 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -16,7 +16,7 @@
 #include <string>
 
 #include "../LogBuffer.h"
-#include "../LogTimes.h"
+#include "../LogReaderThread.h"
 
 // We don't want to waste a lot of entropy on messages
 #define MAX_MSG_LENGTH 5
@@ -94,12 +94,14 @@
     }
 
     LastLogTimes times;
-    LogBuffer log_buffer(&times);
+    LogTags tags;
+    PruneList prune_list;
+    LogBuffer log_buffer(&times, &tags, &prune_list);
     size_t data_left = size;
     const uint8_t** pdata = &data;
 
     log_buffer.enableStatistics();
-    log_buffer.initPrune(nullptr);
+    prune_list.init(nullptr);
     // We want to get pruning code to get called.
     log_id_for_each(i) { log_buffer.setSize(i, 10000); }
 
diff --git a/logd/main.cpp b/logd/main.cpp
index 23bbf86..ceddd0f 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -52,6 +52,7 @@
 #include "LogBuffer.h"
 #include "LogKlog.h"
 #include "LogListener.h"
+#include "LogTags.h"
 #include "LogUtils.h"
 
 #define KMSG_PRIORITY(PRI)                                 \
@@ -108,21 +109,6 @@
     return 0;
 }
 
-// Property helper
-static bool check_flag(const char* prop, const char* flag) {
-    const char* cp = strcasestr(prop, flag);
-    if (!cp) {
-        return false;
-    }
-    // We only will document comma (,)
-    static const char sep[] = ",:;|+ \t\f";
-    if ((cp != prop) && !strchr(sep, cp[-1])) {
-        return false;
-    }
-    cp += strlen(flag);
-    return !*cp || !!strchr(sep, *cp);
-}
-
 static int fdDmesg = -1;
 void android::prdebug(const char* fmt, ...) {
     if (fdDmesg < 0) {
@@ -150,50 +136,6 @@
     }
 }
 
-static sem_t reinit;
-static bool reinit_running = false;
-static LogBuffer* logBuf = nullptr;
-
-static void* reinit_thread_start(void* /*obj*/) {
-    prctl(PR_SET_NAME, "logd.daemon");
-
-    while (reinit_running && !sem_wait(&reinit) && reinit_running) {
-        if (fdDmesg >= 0) {
-            static const char reinit_message[] = { KMSG_PRIORITY(LOG_INFO),
-                                                   'l',
-                                                   'o',
-                                                   'g',
-                                                   'd',
-                                                   '.',
-                                                   'd',
-                                                   'a',
-                                                   'e',
-                                                   'm',
-                                                   'o',
-                                                   'n',
-                                                   ':',
-                                                   ' ',
-                                                   'r',
-                                                   'e',
-                                                   'i',
-                                                   'n',
-                                                   'i',
-                                                   't',
-                                                   '\n' };
-            write(fdDmesg, reinit_message, sizeof(reinit_message));
-        }
-
-        // Anything that reads persist.<property>
-        if (logBuf) {
-            logBuf->init();
-            logBuf->initPrune(nullptr);
-        }
-        android::ReReadEventLogTags();
-    }
-
-    return nullptr;
-}
-
 char* android::uidToName(uid_t u) {
     struct Userdata {
         uid_t uid;
@@ -220,12 +162,6 @@
     return userdata.name;
 }
 
-// Serves as a global method to trigger reinitialization
-// and as a function that can be provided to signal().
-void reinit_signal_handler(int /*signal*/) {
-    sem_post(&reinit);
-}
-
 static void readDmesg(LogAudit* al, LogKlog* kl) {
     if (!al && !kl) {
         return;
@@ -250,10 +186,6 @@
     }
     buf[--len] = '\0';
 
-    if (kl && kl->isMonotonic()) {
-        kl->synchronize(buf.get(), len);
-    }
-
     ssize_t sublen;
     for (char *ptr = nullptr, *tok = buf.get();
          (rc >= 0) && !!(tok = android::log_strntok_r(tok, len, ptr, sublen));
@@ -336,24 +268,10 @@
         return EXIT_FAILURE;
     }
 
-    // Reinit Thread
-    sem_init(&reinit, 0, 0);
-    pthread_attr_t attr;
-    if (!pthread_attr_init(&attr)) {
-        struct sched_param param;
-
-        memset(&param, 0, sizeof(param));
-        pthread_attr_setschedparam(&attr, &param);
-        pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
-        if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
-            pthread_t thread;
-            reinit_running = true;
-            if (pthread_create(&thread, &attr, reinit_thread_start, nullptr)) {
-                reinit_running = false;
-            }
-        }
-        pthread_attr_destroy(&attr);
-    }
+    // A cache of event log tags
+    LogTags log_tags;
+    // Pruning configuration.
+    PruneList prune_list;
 
     // Serves the purpose of managing the last logs times read on a
     // socket connection, and as a reader lock on a range of log
@@ -364,9 +282,7 @@
     // LogBuffer is the object which is responsible for holding all
     // log entries.
 
-    logBuf = new LogBuffer(times);
-
-    signal(SIGHUP, reinit_signal_handler);
+    LogBuffer* logBuf = new LogBuffer(times, &log_tags, &prune_list);
 
     if (__android_logger_property_get_bool(
             "logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
@@ -388,15 +304,14 @@
     // and LogReader is notified to send updates to connected clients.
 
     LogListener* swl = new LogListener(logBuf, reader);
-    // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
-    if (swl->startListener(600)) {
+    if (!swl->StartListener()) {
         return EXIT_FAILURE;
     }
 
     // Command listener listens on /dev/socket/logd for incoming logd
     // administrative commands.
 
-    CommandListener* cl = new CommandListener(logBuf, reader, swl);
+    CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list);
     if (cl->startListener()) {
         return EXIT_FAILURE;
     }
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index d57b79e..55737e9 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -32,6 +32,7 @@
 #include <android-base/stringprintf.h>
 #include <cutils/sockets.h>
 #include <gtest/gtest.h>
+#include <log/log_read.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 #ifdef __ANDROID__
@@ -582,6 +583,7 @@
         "dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=0.000000000");
 }
 
+#ifdef ENABLE_FLAKY_TESTS
 // b/26447386 refined behavior
 TEST(logd, timeout) {
 #ifdef __ANDROID__
@@ -604,7 +606,7 @@
     // A few tries to get it right just in case wrap kicks in due to
     // content providers being active during the test.
     int i = 5;
-    log_time start(android_log_clockid());
+    log_time start(CLOCK_REALTIME);
     start.tv_sec -= 30;  // reach back a moderate period of time
 
     while (--i) {
@@ -680,7 +682,7 @@
             if (msg > start) {
                 start = msg;
                 start.tv_sec += 30;
-                log_time now = log_time(android_log_clockid());
+                log_time now = log_time(CLOCK_REALTIME);
                 if (start > now) {
                     start = now;
                     --start.tv_sec;
@@ -716,6 +718,7 @@
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
+#endif
 
 // b/27242723 confirmed fixed
 TEST(logd, SNDTIMEO) {
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index 405f5a9..5de422f 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -16,6 +16,7 @@
 liblog.so
 libmediandk.so
 libm.so
+libnativehelper.so
 libnativewindow.so
 libneuralnetworks.so nopreload
 libOpenMAXAL.so
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
index b565340..77f8bb8 100644
--- a/rootdir/etc/public.libraries.iot.txt
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -17,6 +17,7 @@
 liblog.so
 libmediandk.so
 libm.so
+libnativehelper.so
 libnativewindow.so
 libneuralnetworks.so
 libOpenMAXAL.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 7cbda08..82196e4 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -16,6 +16,7 @@
 liblog.so
 libmediandk.so
 libm.so
+libnativehelper.so
 libnativewindow.so
 libneuralnetworks.so
 libOpenMAXAL.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 6564e8f..a380ebb 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -78,6 +78,9 @@
     mkdir /dev/boringssl 0755 root root
     mkdir /dev/boringssl/selftest 0755 root root
 
+    # Mount tracefs
+    mount tracefs tracefs /sys/kernel/tracing
+
 # Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
 on early-init && property:ro.product.cpu.abilist32=*
     exec_start boringssl_self_test32
diff --git a/toolbox/start.cpp b/toolbox/start.cpp
index b87ed15..46314cf 100644
--- a/toolbox/start.cpp
+++ b/toolbox/start.cpp
@@ -36,7 +36,12 @@
 }
 
 static void ControlDefaultServices(bool start) {
-    std::vector<std::string> services = {"netd", "surfaceflinger", "zygote"};
+    std::vector<std::string> services = {
+        "netd",
+        "surfaceflinger",
+        "audioserver",
+        "zygote",
+    };
 
     // Only start zygote_secondary if not single arch.
     std::string zygote_configuration = GetProperty("ro.zygote", "");
@@ -86,4 +91,4 @@
 
 extern "C" int stop_main(int argc, char** argv) {
     return StartStop(argc, argv, false);
-}
\ No newline at end of file
+}