Merge "Add new LocalUpdatableMaps benchmarks."
diff --git a/adb/adb.cpp b/adb/adb.cpp
index dcec0ba..08986b7 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -109,7 +109,9 @@
 {
     D("adb: online");
     t->online = 1;
+#if ADB_HOST
     t->SetConnectionEstablished(true);
+#endif
 }
 
 void handle_offline(atransport *t)
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index 43a9252..124e2d8 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -73,6 +73,7 @@
 typedef std::list<std::unique_ptr<alistener>> ListenerList;
 static ListenerList& listener_list GUARDED_BY(listener_list_mutex) = *new ListenerList();
 
+#if ADB_HOST
 static void ss_listener_event_func(int _fd, unsigned ev, void *_l) {
     if (ev & FDE_READ) {
         unique_fd fd(adb_socket_accept(_fd, nullptr, nullptr));
@@ -88,6 +89,7 @@
         }
     }
 }
+#endif
 
 static void listener_event_func(int _fd, unsigned ev, void* _l)
 {
@@ -164,7 +166,7 @@
     }
 }
 
-void enable_daemon_sockets() EXCLUDES(listener_list_mutex) {
+void enable_server_sockets() EXCLUDES(listener_list_mutex) {
     std::lock_guard<std::mutex> lock(listener_list_mutex);
     for (auto& l : listener_list) {
         if (l->connect_to == "*smartsocket*") {
@@ -173,6 +175,7 @@
     }
 }
 
+#if ADB_HOST
 void close_smartsockets() EXCLUDES(listener_list_mutex) {
     std::lock_guard<std::mutex> lock(listener_list_mutex);
     auto pred = [](const std::unique_ptr<alistener>& listener) {
@@ -180,6 +183,7 @@
     };
     listener_list.remove_if(pred);
 }
+#endif
 
 InstallStatus install_listener(const std::string& local_name, const char* connect_to,
                                atransport* transport, int flags, int* resolved_tcp_port,
@@ -188,7 +192,7 @@
     for (auto& l : listener_list) {
         if (local_name == l->local_name) {
             // Can't repurpose a smartsocket.
-            if(l->connect_to[0] == '*') {
+            if (l->connect_to[0] == '*') {
                 *error = "cannot repurpose smartsocket";
                 return INSTALL_STATUS_INTERNAL_ERROR;
             }
@@ -227,7 +231,11 @@
 
     close_on_exec(listener->fd);
     if (listener->connect_to == "*smartsocket*") {
+#if ADB_HOST
         listener->fde = fdevent_create(listener->fd, ss_listener_event_func, listener.get());
+#else
+        LOG(FATAL) << "attempted to connect to *smartsocket* in daemon";
+#endif
     } else {
         listener->fde = fdevent_create(listener->fd, listener_event_func, listener.get());
     }
diff --git a/adb/adb_listeners.h b/adb/adb_listeners.h
index 354dcc5..0aa774a 100644
--- a/adb/adb_listeners.h
+++ b/adb/adb_listeners.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef __ADB_LISTENERS_H
-#define __ADB_LISTENERS_H
+#pragma once
 
 #include "adb.h"
 
@@ -44,7 +43,7 @@
 InstallStatus remove_listener(const char* local_name, atransport* transport);
 void remove_all_listeners(void);
 
-void enable_daemon_sockets();
+#if ADB_HOST
+void enable_server_sockets();
 void close_smartsockets();
-
-#endif /* __ADB_LISTENERS_H */
+#endif
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index c579dde..210241c 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -90,7 +90,7 @@
 int adb_trace_mask;
 
 std::string get_trace_setting() {
-#if ADB_HOST
+#if ADB_HOST || !defined(__ANDROID__)
     const char* setting = getenv("ADB_TRACE");
     if (setting == nullptr) {
         setting = "";
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index f0a287d..efb6c2f 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -1712,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/main.cpp b/adb/client/main.cpp
index 05e210f..a19bd6d 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -206,7 +206,7 @@
         // We don't accept() client connections until this point: this way, clients
         // can't see wonky state early in startup even if they're connecting directly
         // to the server instead of going through the adb program.
-        fdevent_run_on_main_thread([] { enable_daemon_sockets(); });
+        fdevent_run_on_main_thread([] { enable_server_sockets(); });
     });
     notify_thread.detach();
 
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 2b6aa7c..c9993b7 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -144,7 +144,7 @@
         return initialized_;
     }
 
-    virtual ~AsyncServiceRef() {
+    void DestroyServiceRef() {
         if (!initialized_) {
             return;
         }
@@ -152,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_;
 
@@ -203,6 +207,7 @@
         if (ret != kDNSServiceErr_NoError) {
             D("Got %d from DNSServiceGetAddrInfo.", ret);
         } else {
+            D("DNSServiceGetAddrInfo(sdRef=%p, hosttarget=%s)", sdRef_, hosttarget);
             Initialize();
         }
 
@@ -223,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) {
@@ -234,13 +239,13 @@
             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
@@ -264,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()); }
@@ -291,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;
@@ -321,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() {
@@ -348,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();
@@ -366,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();
@@ -393,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);
+        }
     }
 }
 
@@ -459,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) {
@@ -475,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,
diff --git a/adb/coverage/gen_coverage.sh b/adb/coverage/gen_coverage.sh
index cced62a..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,13 +82,16 @@
 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
 
-# Run test_device.py again.
-ANDROID_SERIAL=$REMOTE "$OUTPUT_DIR"/../test_device.py
+# Instead of running test_device.py again, which takes forever, do some I/O back and forth instead.
+dd if=/dev/zero bs=1024 count=10240 | adb -s $REMOTE raw sink:10485760
+adb -s $REMOTE raw source:10485760 | dd of=/dev/null bs=1024 count=10240
 
 # Dump traces again.
 adb disconnect $REMOTE
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 55b7783..db8f07b 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -173,12 +173,6 @@
                 LOG(FATAL) << "Could not set SELinux context";
             }
         }
-        std::string error;
-        std::string local_name =
-            android::base::StringPrintf("tcp:%d", server_port);
-        if (install_listener(local_name, "*smartsocket*", nullptr, 0, nullptr, &error)) {
-            LOG(FATAL) << "Could not install *smartsocket* listener: " << error;
-        }
     }
 }
 #endif
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/fdevent/fdevent.h b/adb/fdevent/fdevent.h
index 9fc3b2c..bb3af74 100644
--- a/adb/fdevent/fdevent.h
+++ b/adb/fdevent/fdevent.h
@@ -79,8 +79,8 @@
     unique_fd Destroy(fdevent* fde);
 
   protected:
-    virtual void Register(fdevent*) {}
-    virtual void Unregister(fdevent*) {}
+    virtual void Register(fdevent*) = 0;
+    virtual void Unregister(fdevent*) = 0;
 
   public:
     // Change which events should cause notifications.
diff --git a/adb/fdevent/fdevent_poll.cpp b/adb/fdevent/fdevent_poll.cpp
index ac86c08..21c1ba0 100644
--- a/adb/fdevent/fdevent_poll.cpp
+++ b/adb/fdevent/fdevent_poll.cpp
@@ -211,3 +211,7 @@
         PLOG(FATAL) << "failed to write to fdevent interrupt fd";
     }
 }
+
+void fdevent_context_poll::Register(fdevent*) {}
+
+void fdevent_context_poll::Unregister(fdevent*) {}
diff --git a/adb/fdevent/fdevent_poll.h b/adb/fdevent/fdevent_poll.h
index 98abab2..8803e3e 100644
--- a/adb/fdevent/fdevent_poll.h
+++ b/adb/fdevent/fdevent_poll.h
@@ -48,6 +48,9 @@
     fdevent_context_poll();
     virtual ~fdevent_context_poll();
 
+    virtual void Register(fdevent* fde) final;
+    virtual void Unregister(fdevent* fde) final;
+
     virtual void Set(fdevent* fde, unsigned events) final;
 
     virtual void Loop() final;
diff --git a/adb/libs/adbconnection/include/adbconnection/process_info.h b/adb/libs/adbconnection/include/adbconnection/process_info.h
index 86d3259..d226699 100644
--- a/adb/libs/adbconnection/include/adbconnection/process_info.h
+++ b/adb/libs/adbconnection/include/adbconnection/process_info.h
@@ -21,7 +21,7 @@
 #include <string>
 
 struct ProcessInfo {
-  const static size_t kMaxArchNameLength = 16;
+  static constexpr size_t kMaxArchNameLength = 16;
 
   uint64_t pid;
   bool debuggable;
diff --git a/adb/socket.h b/adb/socket.h
index 4276851..0623204 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -108,7 +108,10 @@
 
 asocket *create_remote_socket(unsigned id, atransport *t);
 void connect_to_remote(asocket* s, std::string_view destination);
+
+#if ADB_HOST
 void connect_to_smartsocket(asocket *s);
+#endif
 
 // Internal functions that are only made available here for testing purposes.
 namespace internal {
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index d17036c..b7b25fa 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -103,12 +103,6 @@
         if (!android::base::ParseNetAddress(addr, &hostname_value, &port_value, serial, error)) {
             return false;
         }
-
-        if (port_value == -1) {
-            *error = "missing port in specification: ";
-            *error += spec;
-            return false;
-        }
     }
 
     if (hostname) {
diff --git a/adb/socket_spec_test.cpp b/adb/socket_spec_test.cpp
index e9d5270..e83c34c 100644
--- a/adb/socket_spec_test.cpp
+++ b/adb/socket_spec_test.cpp
@@ -24,6 +24,13 @@
 #include <android-base/stringprintf.h>
 #include <gtest/gtest.h>
 
+TEST(socket_spec, parse_tcp_socket_spec_failure) {
+    std::string hostname, error, serial;
+    int port;
+    EXPECT_FALSE(parse_tcp_socket_spec("sneakernet:5037", &hostname, &port, &serial, &error));
+    EXPECT_TRUE(error.find("sneakernet") != std::string::npos);
+}
+
 TEST(socket_spec, parse_tcp_socket_spec_just_port) {
     std::string hostname, error, serial;
     int port;
@@ -134,6 +141,19 @@
     EXPECT_NE(client_fd.get(), -1);
 }
 
+TEST(socket_spec, socket_spec_connect_failure) {
+    std::string error, serial;
+    int port;
+    unique_fd client_fd;
+    EXPECT_FALSE(socket_spec_connect(&client_fd, "tcp:", &port, &serial, &error));
+    EXPECT_FALSE(socket_spec_connect(&client_fd, "acceptfd:", &port, &serial, &error));
+    EXPECT_FALSE(socket_spec_connect(&client_fd, "vsock:", &port, &serial, &error));
+    EXPECT_FALSE(socket_spec_connect(&client_fd, "vsock:x", &port, &serial, &error));
+    EXPECT_FALSE(socket_spec_connect(&client_fd, "vsock:5", &port, &serial, &error));
+    EXPECT_FALSE(socket_spec_connect(&client_fd, "vsock:5:x", &port, &serial, &error));
+    EXPECT_FALSE(socket_spec_connect(&client_fd, "sneakernet:", &port, &serial, &error));
+}
+
 TEST(socket_spec, socket_spec_listen_connect_localfilesystem) {
     std::string error, serial;
     int port;
@@ -152,3 +172,16 @@
         EXPECT_NE(client_fd.get(), -1);
     }
 }
+
+TEST(socket_spec, is_socket_spec) {
+    EXPECT_TRUE(is_socket_spec("tcp:blah"));
+    EXPECT_TRUE(is_socket_spec("acceptfd:blah"));
+    EXPECT_TRUE(is_socket_spec("local:blah"));
+    EXPECT_TRUE(is_socket_spec("localreserved:blah"));
+}
+
+TEST(socket_spec, is_local_socket_spec) {
+    EXPECT_TRUE(is_local_socket_spec("local:blah"));
+    EXPECT_TRUE(is_local_socket_spec("tcp:localhost"));
+    EXPECT_FALSE(is_local_socket_spec("tcp:www.google.com"));
+}
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 423af67..13a4737 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -520,6 +520,7 @@
     send_packet(p, s->transport);
 }
 
+#if ADB_HOST
 /* this is used by magic sockets to rig local sockets to
    send the go-ahead message when they connect */
 static void local_socket_ready_notify(asocket* s) {
@@ -584,8 +585,6 @@
     return n;
 }
 
-#if ADB_HOST
-
 namespace internal {
 
 // Parses a host service string of the following format:
@@ -714,15 +713,11 @@
 
 }  // namespace internal
 
-#endif  // ADB_HOST
-
 static int smart_socket_enqueue(asocket* s, apacket::payload_type data) {
-#if ADB_HOST
     std::string_view service;
     std::string_view serial;
     TransportId transport_id = 0;
     TransportType type = kTransportAny;
-#endif
 
     D("SS(%d): enqueue %zu", s->id, data.size());
 
@@ -755,7 +750,6 @@
 
     D("SS(%d): '%s'", s->id, (char*)(s->smart_socket_data.data() + 4));
 
-#if ADB_HOST
     service = std::string_view(s->smart_socket_data).substr(4);
 
     // TODO: These should be handled in handle_host_request.
@@ -841,16 +835,6 @@
         s2->ready(s2);
         return 0;
     }
-#else /* !ADB_HOST */
-    if (s->transport == nullptr) {
-        std::string error_msg = "unknown failure";
-        s->transport = acquire_one_transport(kTransportAny, nullptr, 0, nullptr, &error_msg);
-        if (s->transport == nullptr) {
-            SendFail(s->peer->fd, error_msg);
-            goto fail;
-        }
-    }
-#endif
 
     if (!s->transport) {
         SendFail(s->peer->fd, "device offline (no transport)");
@@ -922,6 +906,7 @@
     ss->peer = s;
     s->ready(s);
 }
+#endif
 
 size_t asocket::get_max_payload() const {
     size_t max_payload = MAX_PAYLOAD;
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/adb/transport.cpp b/adb/transport.cpp
index 25ed366..1667011 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -928,6 +928,7 @@
     remove_transport(t);
 }
 
+#if ADB_HOST
 static int qual_match(const std::string& to_test, const char* prefix, const std::string& qual,
                       bool sanitize_qual) {
     if (to_test.empty()) /* Return true if both the qual and to_test are empty strings. */
@@ -1083,10 +1084,13 @@
     }
     cv_.notify_one();
 }
+#endif
 
 atransport::~atransport() {
+#if ADB_HOST
     // If the connection callback had not been run before, run it now.
     SetConnectionEstablished(false);
+#endif
 }
 
 int atransport::Write(apacket* p) {
@@ -1240,6 +1244,7 @@
     disconnects_.clear();
 }
 
+#if ADB_HOST
 bool atransport::MatchesTarget(const std::string& target) const {
     if (!serial.empty()) {
         if (target == serial) {
@@ -1283,8 +1288,6 @@
     return reconnect_(this);
 }
 
-#if ADB_HOST
-
 // We use newline as our delimiter, make sure to never output it.
 static std::string sanitize(std::string str, bool alphanumeric) {
     auto pred = alphanumeric ? [](const char c) { return !isalnum(c); }
@@ -1366,7 +1369,7 @@
 void close_usb_devices(bool reset) {
     close_usb_devices([](const atransport*) { return true; }, reset);
 }
-#endif  // ADB_HOST
+#endif
 
 bool register_socket_transport(unique_fd s, std::string serial, int port, int local,
                                atransport::ReconnectCallback reconnect, bool use_tls, int* error) {
@@ -1406,7 +1409,9 @@
 
     lock.unlock();
 
+#if ADB_HOST
     auto waitable = t->connection_waitable();
+#endif
     register_transport(t);
 
     if (local == 1) {
@@ -1414,6 +1419,7 @@
         return true;
     }
 
+#if ADB_HOST
     if (!waitable->WaitForConnection(std::chrono::seconds(10))) {
         if (error) *error = ETIMEDOUT;
         return false;
@@ -1423,6 +1429,7 @@
         if (error) *error = EPERM;
         return false;
     }
+#endif
 
     return true;
 }
@@ -1453,14 +1460,9 @@
             t->Kick();
         }
     }
-#if ADB_HOST
     reconnect_handler.CheckForKicked();
-#endif
 }
 
-#endif
-
-#if ADB_HOST
 void register_usb_transport(usb_handle* usb, const char* serial, const char* devpath,
                             unsigned writeable) {
     atransport* t = new atransport(writeable ? kCsOffline : kCsNoPerm);
@@ -1482,9 +1484,7 @@
 
     register_transport(t);
 }
-#endif
 
-#if ADB_HOST
 // This should only be used for transports with connection_state == kCsNoPerm.
 void unregister_usb_transport(usb_handle* usb) {
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
diff --git a/adb/transport.h b/adb/transport.h
index e93c31c..2ac21cf 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -262,9 +262,12 @@
         : id(NextTransportId()),
           kicked_(false),
           connection_state_(state),
-          connection_waitable_(std::make_shared<ConnectionWaitable>()),
           connection_(nullptr),
           reconnect_(std::move(reconnect)) {
+#if ADB_HOST
+        connection_waitable_ = std::make_shared<ConnectionWaitable>();
+#endif
+
         // Initialize protocol to min version for compatibility with older versions.
         // Version will be updated post-connect.
         protocol_version = A_VERSION_MIN;
@@ -350,6 +353,7 @@
     void RemoveDisconnect(adisconnect* disconnect);
     void RunDisconnects();
 
+#if ADB_HOST
     // Returns true if |target| matches this transport. A matching |target| can be any of:
     //   * <serial>
     //   * <devpath>
@@ -374,6 +378,7 @@
 
     // Attempts to reconnect with the underlying Connection.
     ReconnectResult Reconnect();
+#endif
 
   private:
     std::atomic<bool> kicked_;
@@ -392,9 +397,11 @@
     std::deque<std::shared_ptr<RSA>> keys_;
 #endif
 
+#if ADB_HOST
     // A sharable object that can be used to wait for the atransport's
     // connection to be established.
     std::shared_ptr<ConnectionWaitable> connection_waitable_;
+#endif
 
     // The underlying connection object.
     std::shared_ptr<Connection> connection_ GUARDED_BY(mutex_);
@@ -434,10 +441,17 @@
 void init_transport_registration(void);
 void init_mdns_transport_discovery(void);
 std::string list_transports(bool long_listing);
+
+#if ADB_HOST
 atransport* find_transport(const char* serial);
+
 void kick_all_tcp_devices();
+#endif
+
 void kick_all_transports();
+
 void kick_all_tcp_tls_transports();
+
 #if !ADB_HOST
 void kick_all_transports_by_auth_key(std::string_view auth_key);
 #endif
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index a9ada4a..8579ff4 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -127,6 +127,7 @@
     ASSERT_EQ(std::string("baz"), t.device);
 }
 
+#if ADB_HOST
 TEST_F(TransportTest, test_matches_target) {
     std::string serial = "foo";
     std::string devpath = "/path/to/bar";
@@ -183,3 +184,4 @@
         EXPECT_FALSE(t.MatchesTarget("abc:100.100.100.100"));
     }
 }
+#endif
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 d6b2e25..ab65dd1 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -56,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"
@@ -389,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());
   }
@@ -427,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/Android.bp b/fastboot/Android.bp
index 3a2deb7..6b49fc7 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -149,6 +149,7 @@
     ],
 
     header_libs: [
+        "avb_headers",
         "libsnapshot_headers",
     ]
 }
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index fd6ff8e..1bf4c9c 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -31,6 +31,7 @@
 #include <ext4_utils/ext4_utils.h>
 #include <fs_mgr_overlayfs.h>
 #include <fstab/fstab.h>
+#include <libavb/libavb.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
 #include <libsnapshot/snapshot.h>
@@ -122,6 +123,27 @@
     }
 }
 
+static void CopyAVBFooter(std::vector<char>* data, const uint64_t block_device_size) {
+    if (data->size() < AVB_FOOTER_SIZE) {
+        return;
+    }
+    std::string footer;
+    uint64_t footer_offset = data->size() - AVB_FOOTER_SIZE;
+    for (int idx = 0; idx < AVB_FOOTER_MAGIC_LEN; idx++) {
+        footer.push_back(data->at(footer_offset + idx));
+    }
+    if (0 != footer.compare(AVB_FOOTER_MAGIC)) {
+        return;
+    }
+
+    // copy AVB footer from end of data to end of block device
+    uint64_t original_data_size = data->size();
+    data->resize(block_device_size, 0);
+    for (int idx = 0; idx < AVB_FOOTER_SIZE; idx++) {
+        data->at(block_device_size - 1 - idx) = data->at(original_data_size - 1 - idx);
+    }
+}
+
 int Flash(FastbootDevice* device, const std::string& partition_name) {
     PartitionHandle handle;
     if (!OpenPartition(device, partition_name, &handle)) {
@@ -131,8 +153,14 @@
     std::vector<char> data = std::move(device->download_data());
     if (data.size() == 0) {
         return -EINVAL;
-    } else if (data.size() > get_block_device_size(handle.fd())) {
+    }
+    uint64_t block_device_size = get_block_device_size(handle.fd());
+    if (data.size() > block_device_size) {
         return -EOVERFLOW;
+    } else if (data.size() < block_device_size &&
+               (partition_name == "boot" || partition_name == "boot_a" ||
+                partition_name == "boot_b")) {
+        CopyAVBFooter(&data, block_device_size);
     }
     WipeOverlayfsForPartition(device, partition_name);
     int result = FlashBlockDevice(handle.fd(), data);
diff --git a/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 dc3b985..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;
+        bool ok;
         if (out.alignment) {
-            free_area_start = AlignTo(free_area_start, 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);
+        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);
-    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 52a3217..1a3250a 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -228,8 +228,9 @@
         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);
+        ASSERT_TRUE(AlignTo(lba, device_info.alignment, &aligned_lba));
         EXPECT_EQ(lba, aligned_lba);
     }
 
@@ -1051,3 +1052,17 @@
     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/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 f210eaf..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,16 +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);
+    T to_add = alignment - remainder;
+    if (to_add > std::numeric_limits<T>::max() - base) {
+        return false;
+    }
+    *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 b64861d..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), 64);
-    EXPECT_EQ(AlignTo(32, 32), 32);
-    EXPECT_EQ(AlignTo(17, 32), 32);
+    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/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 5acd039..2d6071f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -580,6 +580,10 @@
     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_;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 03efd68..488009a 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -1691,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;
     }
@@ -1788,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;
         }
@@ -2364,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,
@@ -2389,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();
         }
@@ -2742,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/init/reboot.cpp b/init/reboot.cpp
index e89f74a..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;
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/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/log/log.h b/liblog/include/log/log.h
index 820b7cb..d7e9b7d 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -133,12 +133,6 @@
   (void)__android_log_bswrite(_tag, _value);
 #endif
 
-#ifdef __linux__
-
-clockid_t android_log_clockid(void);
-
-#endif /* __linux__ */
-
 /* --------------------------------------------------------------------- */
 
 /*
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index 24b88d2..23d76f4 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -112,7 +112,6 @@
 
 /* Multiple log_id_t opens */
 struct logger* android_logger_open(struct logger_list* logger_list, log_id_t id);
-#define android_logger_close android_logger_free
 /* Single log_id_t open */
 struct logger_list* android_logger_list_open(log_id_t id, int mode,
                                              unsigned int tail, pid_t pid);
diff --git a/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 9e8d277..238431f 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -216,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;
@@ -1465,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 0751e2c..8e676bd 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -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/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index f4734b9..a4e4def 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -184,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;
@@ -260,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;
@@ -335,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;
@@ -410,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;
@@ -483,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;
@@ -792,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 d3d8e91..bbc985a 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -329,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) {
@@ -432,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/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/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
index 766ea0f..ccc6f62 100644
--- a/libprocessgroup/profiles/Android.bp
+++ b/libprocessgroup/profiles/Android.bp
@@ -89,15 +89,15 @@
         "test_vendor.cpp",
     ],
     static_libs: [
+        "libbase",
         "libgmock",
+        "liblog",
+        "libjsoncpp",
         "libjsonpbverify",
         "libjsonpbparse",
         "libprocessgroup_proto",
     ],
     shared_libs: [
-        "libbase",
-        "liblog",
-        "libjsoncpp",
         "libprotobuf-cpp-full",
     ],
     test_suites: [
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index ff886fd..db61669 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -8,6 +8,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "apex_inherit",
     export_include_dirs: ["include"],
 
     target: {
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 627f0d4..3b98bab 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -43,6 +43,7 @@
         "//apex_available:anyapex",
         "//apex_available:platform",
     ],
+    min_sdk_version: "apex_inherit",
 }
 
 cc_test {
diff --git a/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/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/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/libutils/Android.bp b/libutils/Android.bp
index 3ab619b..2dbfb70 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -22,6 +22,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "apex_inherit",
 
     header_libs: [
         "liblog_headers",
@@ -162,6 +163,7 @@
         "//apex_available:anyapex",
         "//apex_available:platform",
     ],
+    min_sdk_version: "apex_inherit",
 }
 
 cc_library {
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/logcat/logcat.cpp b/logcat/logcat.cpp
index 8185f01..13023f2 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -459,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 2663271..ee86566 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -33,12 +33,13 @@
 
     srcs: [
         "LogCommand.cpp",
+        "ChattyLogBuffer.cpp",
         "CommandListener.cpp",
         "LogListener.cpp",
         "LogReader.cpp",
-        "LogBuffer.cpp",
+        "LogReaderList.cpp",
+        "LogReaderThread.cpp",
         "LogBufferElement.cpp",
-        "LogTimes.cpp",
         "LogStatistics.cpp",
         "LogWhiteBlackList.cpp",
         "libaudit.c",
@@ -52,7 +53,14 @@
 
     export_include_dirs: ["."],
 
-    cflags: ["-Werror"] + event_flag,
+    cflags: [
+        "-Wextra",
+        "-Wthread-safety",
+    ] + event_flag,
+
+    lto: {
+        thin: true
+    }
 }
 
 cc_binary {
@@ -75,7 +83,13 @@
         "libcap",
     ],
 
-    cflags: ["-Werror"],
+    cflags: [
+        "-Wextra",
+    ],
+
+    lto: {
+        thin: true
+    }
 }
 
 cc_binary {
@@ -90,10 +104,8 @@
     shared_libs: ["libbase"],
 
     cflags: [
-        "-Wall",
+        "-Wconversion",
         "-Wextra",
-        "-Werror",
-        "-Wconversion"
     ],
 }
 
diff --git a/logd/LogBuffer.cpp b/logd/ChattyLogBuffer.cpp
similarity index 72%
rename from logd/LogBuffer.cpp
rename to logd/ChattyLogBuffer.cpp
index a3e4e09..fa5bcee 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/ChattyLogBuffer.cpp
@@ -13,9 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// for manual checking of stale entries during LogBuffer::erase()
+// for manual checking of stale entries during ChattyLogBuffer::erase()
 //#define DEBUG_CHECK_FOR_STALE_ENTRIES
 
+#include "ChattyLogBuffer.h"
+
 #include <ctype.h>
 #include <endian.h>
 #include <errno.h>
@@ -32,7 +34,6 @@
 #include <cutils/properties.h>
 #include <private/android_logger.h>
 
-#include "LogBuffer.h"
 #include "LogKlog.h"
 #include "LogReader.h"
 #include "LogUtils.h"
@@ -44,72 +45,22 @@
 // Default
 #define log_buffer_size(id) mMaxSize[id]
 
-void LogBuffer::init() {
+void ChattyLogBuffer::Init() {
     log_id_for_each(i) {
-        if (setSize(i, __android_logger_get_buffer_size(i))) {
-            setSize(i, LOG_BUFFER_MIN_SIZE);
+        if (SetSize(i, __android_logger_get_buffer_size(i))) {
+            SetSize(i, LOG_BUFFER_MIN_SIZE);
         }
     }
-    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();
-    }
-
     // Release any sleeping reader threads to dump their current content.
-    LogTimeEntry::wrlock();
-
-    LastLogTimes::iterator times = mTimes.begin();
-    while (times != mTimes.end()) {
-        LogTimeEntry* entry = times->get();
-        entry->triggerReader_Locked();
-        times++;
+    auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
+    for (const auto& reader_thread : reader_list_->reader_threads()) {
+        reader_thread->triggerReader_Locked();
     }
-
-    LogTimeEntry::unlock();
 }
 
-LogBuffer::LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune)
-    : monotonic(android_log_clockid() == CLOCK_MONOTONIC),
-      mTimes(*times),
-      tags_(tags),
-      prune_(prune) {
+ChattyLogBuffer::ChattyLogBuffer(LogReaderList* reader_list, LogTags* tags, PruneList* prune,
+                                 LogStatistics* stats)
+    : reader_list_(reader_list), tags_(tags), prune_(prune), stats_(stats) {
     pthread_rwlock_init(&mLogElementsLock, nullptr);
 
     log_id_for_each(i) {
@@ -117,17 +68,17 @@
         droppedElements[i] = nullptr;
     }
 
-    init();
+    Init();
 }
 
-LogBuffer::~LogBuffer() {
+ChattyLogBuffer::~ChattyLogBuffer() {
     log_id_for_each(i) {
         delete lastLoggedElements[i];
         delete droppedElements[i];
     }
 }
 
-LogBufferElementCollection::iterator LogBuffer::GetOldest(log_id_t log_id) {
+LogBufferElementCollection::iterator ChattyLogBuffer::GetOldest(log_id_t log_id) {
     auto it = mLogElements.begin();
     if (oldest_[log_id]) {
         it = *oldest_[log_id];
@@ -143,8 +94,7 @@
 
 enum match_type { DIFFERENT, SAME, SAME_LIBLOG };
 
-static enum match_type identical(LogBufferElement* elem,
-                                 LogBufferElement* last) {
+static enum match_type identical(LogBufferElement* elem, LogBufferElement* last) {
     // is it mostly identical?
     //  if (!elem) return DIFFERENT;
     ssize_t lenl = elem->getMsgLen();
@@ -158,8 +108,7 @@
     if (elem->getTid() != last->getTid()) return DIFFERENT;
 
     // last is more than a minute old, stop squashing identical messages
-    if (elem->getRealTime().nsec() >
-        (last->getRealTime().nsec() + 60 * NS_PER_SEC))
+    if (elem->getRealTime().nsec() > (last->getRealTime().nsec() + 60 * NS_PER_SEC))
         return DIFFERENT;
 
     // Identical message
@@ -168,21 +117,17 @@
     if (lenl == lenr) {
         if (!fastcmp<memcmp>(msgl, msgr, lenl)) return SAME;
         // liblog tagged messages (content gets summed)
-        if ((elem->getLogId() == LOG_ID_EVENTS) &&
-            (lenl == sizeof(android_log_event_int_t)) &&
-            !fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_int_t) -
-                                             sizeof(int32_t)) &&
-            (elem->getTag() == LIBLOG_LOG_TAG)) {
+        if (elem->getLogId() == LOG_ID_EVENTS && lenl == sizeof(android_log_event_int_t) &&
+            !fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_int_t) - sizeof(int32_t)) &&
+            elem->getTag() == LIBLOG_LOG_TAG) {
             return SAME_LIBLOG;
         }
     }
 
     // audit message (except sequence number) identical?
-    if (last->isBinary() &&
-        (lenl > static_cast<ssize_t>(sizeof(android_log_event_string_t))) &&
-        (lenr > static_cast<ssize_t>(sizeof(android_log_event_string_t)))) {
-        if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) -
-                                            sizeof(int32_t))) {
+    if (last->isBinary() && lenl > static_cast<ssize_t>(sizeof(android_log_event_string_t)) &&
+        lenr > static_cast<ssize_t>(sizeof(android_log_event_string_t))) {
+        if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) - sizeof(int32_t))) {
             return DIFFERENT;
         }
         msgl += sizeof(android_log_event_string_t);
@@ -198,15 +143,14 @@
     if (!avcr) return DIFFERENT;
     lenr -= avcr - msgr;
     if (lenl != lenr) return DIFFERENT;
-    if (fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc),
-                        lenl - strlen(avc))) {
+    if (fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc), lenl - strlen(avc))) {
         return DIFFERENT;
     }
     return SAME;
 }
 
-int LogBuffer::log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
-                   pid_t tid, const char* msg, uint16_t len) {
+int ChattyLogBuffer::Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
+                         const char* msg, uint16_t len) {
     if (log_id >= LOG_ID_MAX) {
         return -EINVAL;
     }
@@ -242,9 +186,7 @@
     }
     if (!__android_log_is_loggable_len(prio, tag, tag_len, ANDROID_LOG_VERBOSE)) {
         // Log traffic received to total
-        wrlock();
-        stats.addTotal(elem);
-        unlock();
+        stats_->AddTotal(elem);
         delete elem;
         return -EACCES;
     }
@@ -330,8 +272,7 @@
             if (dropped) {
                 // Sum up liblog tag messages?
                 if ((count == 0) /* at Pass 1 */ && (match == SAME_LIBLOG)) {
-                    android_log_event_int_t* event =
-                        reinterpret_cast<android_log_event_int_t*>(
+                    android_log_event_int_t* event = reinterpret_cast<android_log_event_int_t*>(
                             const_cast<char*>(currentLast->getMsg()));
                     //
                     // To unit test, differentiate with something like:
@@ -342,7 +283,7 @@
                     uint32_t swab = event->payload.data;
                     unsigned long long total = htole32(swab);
                     event = reinterpret_cast<android_log_event_int_t*>(
-                        const_cast<char*>(elem->getMsg()));
+                            const_cast<char*>(elem->getMsg()));
                     swab = event->payload.data;
 
                     lastLoggedElements[LOG_ID_EVENTS] = elem;
@@ -353,7 +294,7 @@
                         unlock();
                         return len;
                     }
-                    stats.addTotal(currentLast);
+                    stats_->AddTotal(currentLast);
                     delete currentLast;
                     swab = total;
                     event->payload.data = htole32(swab);
@@ -369,7 +310,7 @@
                 }
             }
             if (count) {
-                stats.addTotal(currentLast);
+                stats_->AddTotal(currentLast);
                 currentLast->setDropped(count);
             }
             droppedElements[log_id] = currentLast;
@@ -397,39 +338,24 @@
     return len;
 }
 
-// assumes LogBuffer::wrlock() held, owns elem, look after garbage collection
-void LogBuffer::log(LogBufferElement* elem) {
+// assumes ChattyLogBuffer::wrlock() held, owns elem, look after garbage collection
+void ChattyLogBuffer::log(LogBufferElement* elem) {
     mLogElements.push_back(elem);
-    stats.add(elem);
+    stats_->Add(elem);
     maybePrune(elem->getLogId());
+    reader_list_->NotifyNewLog(1 << elem->getLogId());
 }
 
-// Prune at most 10% of the log entries or maxPrune, whichever is less.
-//
-// LogBuffer::wrlock() must be held when this function is called.
-void LogBuffer::maybePrune(log_id_t id) {
-    size_t sizes = stats.sizes(id);
-    unsigned long maxSize = log_buffer_size(id);
-    if (sizes > maxSize) {
-        size_t sizeOver = sizes - ((maxSize * 9) / 10);
-        size_t elements = stats.realElements(id);
-        size_t minElements = elements / 100;
-        if (minElements < minPrune) {
-            minElements = minPrune;
-        }
-        unsigned long pruneRows = elements * sizeOver / sizes;
-        if (pruneRows < minElements) {
-            pruneRows = minElements;
-        }
-        if (pruneRows > maxPrune) {
-            pruneRows = maxPrune;
-        }
-        prune(id, pruneRows);
+// ChattyLogBuffer::wrlock() must be held when this function is called.
+void ChattyLogBuffer::maybePrune(log_id_t id) {
+    unsigned long prune_rows;
+    if (stats_->ShouldPrune(id, log_buffer_size(id), &prune_rows)) {
+        prune(id, prune_rows);
     }
 }
 
-LogBufferElementCollection::iterator LogBuffer::erase(
-    LogBufferElementCollection::iterator it, bool coalesce) {
+LogBufferElementCollection::iterator ChattyLogBuffer::erase(LogBufferElementCollection::iterator it,
+                                                            bool coalesce) {
     LogBufferElement* element = *it;
     log_id_t id = element->getLogId();
 
@@ -437,9 +363,8 @@
     // after the element is erased from the main logging list.
 
     {  // start of scope for found iterator
-        int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
-                      ? element->getTag()
-                      : element->getUid();
+        int key = (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element->getTag()
+                                                                 : element->getUid();
         LogBufferIteratorMap::iterator found = mLastWorst[id].find(key);
         if ((found != mLastWorst[id].end()) && (it == found->second)) {
             mLastWorst[id].erase(found);
@@ -450,10 +375,8 @@
         // element->getUid() may not be AID_SYSTEM for next-best-watermark.
         // will not assume id != LOG_ID_EVENTS or LOG_ID_SECURITY for KISS and
         // long term code stability, find() check should be fast for those ids.
-        LogBufferPidIteratorMap::iterator found =
-            mLastWorstPidOfSystem[id].find(element->getPid());
-        if ((found != mLastWorstPidOfSystem[id].end()) &&
-            (it == found->second)) {
+        LogBufferPidIteratorMap::iterator found = mLastWorstPidOfSystem[id].find(element->getPid());
+        if (found != mLastWorstPidOfSystem[id].end() && it == found->second) {
             mLastWorstPidOfSystem[id].erase(found);
         }
     }
@@ -463,9 +386,8 @@
     log_id_for_each(i) { doSetLast |= setLast[i] = oldest_[i] && it == *oldest_[i]; }
 #ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
     LogBufferElementCollection::iterator bad = it;
-    int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
-                  ? element->getTag()
-                  : element->getUid();
+    int key =
+            (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element->getTag() : element->getUid();
 #endif
     it = mLogElements.erase(it);
     if (doSetLast) {
@@ -484,22 +406,20 @@
     log_id_for_each(i) {
         for (auto b : mLastWorst[i]) {
             if (bad == b.second) {
-                android::prdebug("stale mLastWorst[%d] key=%d mykey=%d\n", i,
-                                 b.first, key);
+                android::prdebug("stale mLastWorst[%d] key=%d mykey=%d\n", i, b.first, key);
             }
         }
         for (auto b : mLastWorstPidOfSystem[i]) {
             if (bad == b.second) {
-                android::prdebug("stale mLastWorstPidOfSystem[%d] pid=%d\n", i,
-                                 b.first);
+                android::prdebug("stale mLastWorstPidOfSystem[%d] pid=%d\n", i, b.first);
             }
         }
     }
 #endif
     if (coalesce) {
-        stats.erase(element);
+        stats_->Erase(element);
     } else {
-        stats.subtract(element);
+        stats_->Subtract(element);
     }
     delete element;
 
@@ -519,26 +439,20 @@
         uint64_t value;
     } __packed;
 
-   public:
-    LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid)
-        : uid(uid), pid(pid), tid(tid) {
-    }
-    explicit LogBufferElementKey(uint64_t key) : value(key) {
-    }
+  public:
+    LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid) : uid(uid), pid(pid), tid(tid) {}
+    explicit LogBufferElementKey(uint64_t key) : value(key) {}
 
-    uint64_t getKey() {
-        return value;
-    }
+    uint64_t getKey() { return value; }
 };
 
 class LogBufferElementLast {
     typedef std::unordered_map<uint64_t, LogBufferElement*> LogBufferElementMap;
     LogBufferElementMap map;
 
-   public:
+  public:
     bool coalesce(LogBufferElement* element, uint16_t dropped) {
-        LogBufferElementKey key(element->getUid(), element->getPid(),
-                                element->getTid());
+        LogBufferElementKey key(element->getUid(), element->getPid(), element->getTid());
         LogBufferElementMap::iterator it = map.find(key.getKey());
         if (it != map.end()) {
             LogBufferElement* found = it->second;
@@ -554,14 +468,11 @@
     }
 
     void add(LogBufferElement* element) {
-        LogBufferElementKey key(element->getUid(), element->getPid(),
-                                element->getTid());
+        LogBufferElementKey key(element->getUid(), element->getPid(), element->getTid());
         map[key.getKey()] = element;
     }
 
-    inline void clear() {
-        map.clear();
-    }
+    void clear() { map.clear(); }
 
     void clear(LogBufferElement* element) {
         uint64_t current = element->getRealTime().nsec() - (EXPIRE_RATELIMIT * NS_PER_SEC);
@@ -579,22 +490,21 @@
 
 // If the selected reader is blocking our pruning progress, decide on
 // what kind of mitigation is necessary to unblock the situation.
-void LogBuffer::kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows) {
-    if (stats.sizes(id) > (2 * log_buffer_size(id))) {  // +100%
+void ChattyLogBuffer::kickMe(LogReaderThread* me, log_id_t id, unsigned long pruneRows) {
+    if (stats_->Sizes(id) > (2 * log_buffer_size(id))) {  // +100%
         // A misbehaving or slow reader has its connection
         // dropped if we hit too much memory pressure.
-        android::prdebug("Kicking blocked reader, pid %d, from LogBuffer::kickMe()\n",
-                         me->mClient->getPid());
+        android::prdebug("Kicking blocked reader, pid %d, from ChattyLogBuffer::kickMe()\n",
+                         me->client()->getPid());
         me->release_Locked();
-    } else if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
-        // Allow a blocked WRAP timeout reader to
-        // trigger and start reporting the log data.
+    } else if (me->deadline().time_since_epoch().count() != 0) {
+        // Allow a blocked WRAP deadline reader to trigger and start reporting the log data.
         me->triggerReader_Locked();
     } else {
         // tell slow reader to skip entries to catch up
         android::prdebug(
-                "Skipping %lu entries from slow reader, pid %d, from LogBuffer::kickMe()\n",
-                pruneRows, me->mClient->getPid());
+                "Skipping %lu entries from slow reader, pid %d, from ChattyLogBuffer::kickMe()\n",
+                pruneRows, me->client()->getPid());
         me->triggerSkip_Locked(id, pruneRows);
     }
 }
@@ -644,26 +554,25 @@
 // The third thread is optional, and only gets hit if there was a whitelist
 // and more needs to be pruned against the backstop of the region lock.
 //
-// LogBuffer::wrlock() must be held when this function is called.
+// ChattyLogBuffer::wrlock() must be held when this function is called.
 //
-bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
-    LogTimeEntry* oldest = nullptr;
+bool ChattyLogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
+    LogReaderThread* oldest = nullptr;
     bool busy = false;
     bool clearAll = pruneRows == ULONG_MAX;
 
-    LogTimeEntry::rdlock();
+    auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
 
     // 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)))) {
-            oldest = entry;
+    for (const auto& reader_thread : reader_list_->reader_threads()) {
+        if (!reader_thread->IsWatching(id)) {
+            continue;
         }
-        times++;
+        if (!oldest || oldest->start() > reader_thread->start() ||
+            (oldest->start() == reader_thread->start() &&
+             reader_thread->deadline().time_since_epoch().count() != 0)) {
+            oldest = reader_thread.get();
+        }
     }
 
     LogBufferElementCollection::iterator it;
@@ -675,13 +584,12 @@
         while (it != mLogElements.end()) {
             LogBufferElement* element = *it;
 
-            if ((element->getLogId() != id) ||
-                (element->getUid() != caller_uid)) {
+            if (element->getLogId() != id || element->getUid() != caller_uid) {
                 ++it;
                 continue;
             }
 
-            if (oldest && oldest->mStart <= element->getSequence()) {
+            if (oldest && oldest->start() <= element->getSequence()) {
                 busy = true;
                 kickMe(oldest, id, pruneRows);
                 break;
@@ -692,7 +600,6 @@
                 break;
             }
         }
-        LogTimeEntry::unlock();
         return busy;
     }
 
@@ -709,19 +616,14 @@
             // Calculate threshold as 12.5% of available storage
             size_t threshold = log_buffer_size(id) / 8;
 
-            if ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY)) {
-                stats.sortTags(AID_ROOT, (pid_t)0, 2, id)
-                    .findWorst(worst, worst_sizes, second_worst_sizes,
-                               threshold);
+            if (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) {
+                stats_->WorstTwoTags(threshold, &worst, &worst_sizes, &second_worst_sizes);
                 // per-pid filter for AID_SYSTEM sources is too complex
             } else {
-                stats.sort(AID_ROOT, (pid_t)0, 2, id)
-                    .findWorst(worst, worst_sizes, second_worst_sizes,
-                               threshold);
+                stats_->WorstTwoUids(id, threshold, &worst, &worst_sizes, &second_worst_sizes);
 
-                if ((worst == AID_SYSTEM) && prune_->worstPidOfSystemEnabled()) {
-                    stats.sortPids(worst, (pid_t)0, 2, id)
-                        .findWorst(worstPid, worst_sizes, second_worst_sizes);
+                if (worst == AID_SYSTEM && prune_->worstPidOfSystemEnabled()) {
+                    stats_->WorstTwoSystemPids(id, worst_sizes, &worstPid, &second_worst_sizes);
                 }
             }
         }
@@ -741,10 +643,8 @@
         bool gc = pruneRows <= 1;
         if (!gc && (worst != -1)) {
             {  // begin scope for worst found iterator
-                LogBufferIteratorMap::iterator found =
-                    mLastWorst[id].find(worst);
-                if ((found != mLastWorst[id].end()) &&
-                    (found->second != mLogElements.end())) {
+                LogBufferIteratorMap::iterator found = mLastWorst[id].find(worst);
+                if (found != mLastWorst[id].end() && found->second != mLogElements.end()) {
                     leading = false;
                     it = found->second;
                 }
@@ -752,10 +652,9 @@
             if (worstPid) {  // begin scope for pid worst found iterator
                 // FYI: worstPid only set if !LOG_ID_EVENTS and
                 //      !LOG_ID_SECURITY, not going to make that assumption ...
-                LogBufferPidIteratorMap::iterator found =
-                    mLastWorstPidOfSystem[id].find(worstPid);
-                if ((found != mLastWorstPidOfSystem[id].end()) &&
-                    (found->second != mLogElements.end())) {
+                LogBufferPidIteratorMap::iterator found = mLastWorstPidOfSystem[id].find(worstPid);
+                if (found != mLastWorstPidOfSystem[id].end() &&
+                    found->second != mLogElements.end()) {
                     leading = false;
                     it = found->second;
                 }
@@ -764,7 +663,7 @@
         if (leading) {
             it = GetOldest(id);
         }
-        static const timespec too_old = { EXPIRE_HOUR_THRESHOLD * 60 * 60, 0 };
+        static const timespec too_old = {EXPIRE_HOUR_THRESHOLD * 60 * 60, 0};
         LogBufferElementCollection::iterator lastt;
         lastt = mLogElements.end();
         --lastt;
@@ -772,7 +671,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;
@@ -797,9 +696,8 @@
                 continue;
             }
 
-            int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
-                          ? element->getTag()
-                          : element->getUid();
+            int key = (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element->getTag()
+                                                                     : element->getUid();
 
             if (hasBlacklist && prune_->naughty(element)) {
                 last.clear(element);
@@ -830,10 +728,9 @@
 
             if (dropped) {
                 last.add(element);
-                if (worstPid &&
-                    ((!gc && (element->getPid() == worstPid)) ||
-                     (mLastWorstPidOfSystem[id].find(element->getPid()) ==
-                      mLastWorstPidOfSystem[id].end()))) {
+                if (worstPid && ((!gc && element->getPid() == worstPid) ||
+                                 mLastWorstPidOfSystem[id].find(element->getPid()) ==
+                                         mLastWorstPidOfSystem[id].end())) {
                     // element->getUid() may not be AID_SYSTEM, next best
                     // watermark if current one empty. id is not LOG_ID_EVENTS
                     // or LOG_ID_SECURITY because of worstPid check.
@@ -847,8 +744,7 @@
                 continue;
             }
 
-            if ((key != worst) ||
-                (worstPid && (element->getPid() != worstPid))) {
+            if (key != worst || (worstPid && element->getPid() != worstPid)) {
                 leading = false;
                 last.clear(element);
                 ++it;
@@ -870,22 +766,20 @@
             if (leading) {
                 it = erase(it);
             } else {
-                stats.drop(element);
+                stats_->Drop(element);
                 element->setDropped(1);
                 if (last.coalesce(element, 1)) {
                     it = erase(it, true);
                 } else {
                     last.add(element);
-                    if (worstPid &&
-                        (!gc || (mLastWorstPidOfSystem[id].find(worstPid) ==
-                                 mLastWorstPidOfSystem[id].end()))) {
+                    if (worstPid && (!gc || mLastWorstPidOfSystem[id].find(worstPid) ==
+                                                    mLastWorstPidOfSystem[id].end())) {
                         // element->getUid() may not be AID_SYSTEM, next best
                         // watermark if current one empty. id is not
                         // LOG_ID_EVENTS or LOG_ID_SECURITY because of worstPid.
                         mLastWorstPidOfSystem[id][worstPid] = it;
                     }
-                    if ((!gc && !worstPid) ||
-                        (mLastWorst[id].find(worst) == mLastWorst[id].end())) {
+                    if ((!gc && !worstPid) || mLastWorst[id].find(worst) == mLastWorst[id].end()) {
                         mLastWorst[id][worst] = it;
                     }
                     ++it;
@@ -914,7 +808,7 @@
             continue;
         }
 
-        if (oldest && oldest->mStart <= element->getSequence()) {
+        if (oldest && oldest->start() <= element->getSequence()) {
             busy = true;
             if (!whitelist) kickMe(oldest, id, pruneRows);
             break;
@@ -942,7 +836,7 @@
                 continue;
             }
 
-            if (oldest && oldest->mStart <= element->getSequence()) {
+            if (oldest && oldest->start() <= element->getSequence()) {
                 busy = true;
                 kickMe(oldest, id, pruneRows);
                 break;
@@ -953,13 +847,11 @@
         }
     }
 
-    LogTimeEntry::unlock();
-
     return (pruneRows > 0) && busy;
 }
 
 // clear all rows of type "id" from the buffer.
-bool LogBuffer::clear(log_id_t id, uid_t uid) {
+bool ChattyLogBuffer::Clear(log_id_t id, uid_t uid) {
     bool busy = true;
     // If it takes more than 4 tries (seconds) to clear, then kill reader(s)
     for (int retry = 4;;) {
@@ -976,20 +868,15 @@
             // readers and let the clear run (below) deal with determining
             // if we are still blocked and return an error code to caller.
             if (busy) {
-                LogTimeEntry::wrlock();
-                LastLogTimes::iterator times = mTimes.begin();
-                while (times != mTimes.end()) {
-                    LogTimeEntry* entry = times->get();
-                    // Killer punch
-                    if (entry->isWatching(id)) {
+                auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
+                for (const auto& reader_thread : reader_list_->reader_threads()) {
+                    if (reader_thread->IsWatching(id)) {
                         android::prdebug(
-                                "Kicking blocked reader, pid %d, from LogBuffer::clear()\n",
-                                entry->mClient->getPid());
-                        entry->release_Locked();
+                                "Kicking blocked reader, pid %d, from ChattyLogBuffer::clear()\n",
+                                reader_thread->client()->getPid());
+                        reader_thread->release_Locked();
                     }
-                    times++;
                 }
-                LogTimeEntry::unlock();
             }
         }
         wrlock();
@@ -1003,16 +890,8 @@
     return busy;
 }
 
-// get the used space associated with "id".
-unsigned long LogBuffer::getSizeUsed(log_id_t id) {
-    rdlock();
-    size_t retval = stats.sizes(id);
-    unlock();
-    return retval;
-}
-
 // set the total space allocated to "id"
-int LogBuffer::setSize(log_id_t id, unsigned long size) {
+int ChattyLogBuffer::SetSize(log_id_t id, unsigned long size) {
     // Reasonable limits ...
     if (!__android_logger_valid_buffer_size(size)) {
         return -1;
@@ -1024,16 +903,16 @@
 }
 
 // get the total space allocated to "id"
-unsigned long LogBuffer::getSize(log_id_t id) {
+unsigned long ChattyLogBuffer::GetSize(log_id_t id) {
     rdlock();
     size_t retval = log_buffer_size(id);
     unlock();
     return retval;
 }
 
-uint64_t LogBuffer::flushTo(SocketClient* reader, uint64_t start, pid_t* lastTid, bool privileged,
-                            bool security,
-                            int (*filter)(const LogBufferElement* element, void* arg), void* arg) {
+uint64_t ChattyLogBuffer::FlushTo(
+        SocketClient* reader, uint64_t start, pid_t* lastTid, bool privileged, bool security,
+        const std::function<FlushToResult(const LogBufferElement* element)>& filter) {
     LogBufferElementCollection::iterator it;
     uid_t uid = reader->getUid();
 
@@ -1071,11 +950,11 @@
 
         // NB: calling out to another object with wrlock() held (safe)
         if (filter) {
-            int ret = (*filter)(element, arg);
-            if (ret == false) {
+            FlushToResult ret = filter(element);
+            if (ret == FlushToResult::kSkip) {
                 continue;
             }
-            if (ret != true) {
+            if (ret == FlushToResult::kStop) {
                 break;
             }
         }
@@ -1089,13 +968,13 @@
             // is due to spam filter.  chatty to chatty of different
             // source is also due to spam filter.
             lastTid[element->getLogId()] =
-                (element->getDropped() && !sameTid) ? 0 : element->getTid();
+                    (element->getDropped() && !sameTid) ? 0 : element->getTid();
         }
 
         unlock();
 
         // range locking in LastLogTimes looks after us
-        curr = element->flushTo(reader, this, sameTid);
+        curr = element->flushTo(reader, stats_, sameTid);
 
         if (curr == element->FLUSH_ERROR) {
             return curr;
@@ -1107,14 +986,3 @@
 
     return curr;
 }
-
-std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,
-                                        unsigned int logMask) {
-    wrlock();
-
-    std::string ret = stats.format(uid, pid, logMask);
-
-    unlock();
-
-    return ret;
-}
diff --git a/logd/ChattyLogBuffer.h b/logd/ChattyLogBuffer.h
new file mode 100644
index 0000000..d9cd24f
--- /dev/null
+++ b/logd/ChattyLogBuffer.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <list>
+#include <optional>
+#include <string>
+
+#include <android/log.h>
+#include <private/android_filesystem_config.h>
+#include <sysutils/SocketClient.h>
+
+#include "LogBuffer.h"
+#include "LogBufferElement.h"
+#include "LogStatistics.h"
+#include "LogTags.h"
+#include "LogWhiteBlackList.h"
+
+typedef std::list<LogBufferElement*> LogBufferElementCollection;
+
+class LogReaderList;
+class LogReaderThread;
+
+class ChattyLogBuffer : public LogBuffer {
+    LogBufferElementCollection mLogElements;
+    pthread_rwlock_t mLogElementsLock;
+
+    // watermark of any worst/chatty uid processing
+    typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator> LogBufferIteratorMap;
+    LogBufferIteratorMap mLastWorst[LOG_ID_MAX];
+    // watermark of any worst/chatty pid of system processing
+    typedef std::unordered_map<pid_t, LogBufferElementCollection::iterator> LogBufferPidIteratorMap;
+    LogBufferPidIteratorMap mLastWorstPidOfSystem[LOG_ID_MAX];
+
+    unsigned long mMaxSize[LOG_ID_MAX];
+
+    LogBufferElement* lastLoggedElements[LOG_ID_MAX];
+    LogBufferElement* droppedElements[LOG_ID_MAX];
+    void log(LogBufferElement* elem);
+
+  public:
+    ChattyLogBuffer(LogReaderList* reader_list, LogTags* tags, PruneList* prune,
+                    LogStatistics* stats);
+    ~ChattyLogBuffer();
+    void Init() override;
+
+    int Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg,
+            uint16_t len) override;
+    uint64_t FlushTo(
+            SocketClient* writer, uint64_t start, pid_t* lastTid, bool privileged, bool security,
+            const std::function<FlushToResult(const LogBufferElement* element)>& filter) override;
+
+    bool Clear(log_id_t id, uid_t uid = AID_ROOT) override;
+    unsigned long GetSize(log_id_t id) override;
+    int SetSize(log_id_t id, unsigned long size) override;
+
+  private:
+    void wrlock() { pthread_rwlock_wrlock(&mLogElementsLock); }
+    void rdlock() { pthread_rwlock_rdlock(&mLogElementsLock); }
+    void unlock() { pthread_rwlock_unlock(&mLogElementsLock); }
+
+    void maybePrune(log_id_t id);
+    void kickMe(LogReaderThread* me, log_id_t id, unsigned long pruneRows);
+
+    bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
+    LogBufferElementCollection::iterator erase(LogBufferElementCollection::iterator it,
+                                               bool coalesce = false);
+
+    // Returns an iterator to the oldest element for a given log type, or mLogElements.end() if
+    // there are no logs for the given log type. Requires mLogElementsLock to be held.
+    LogBufferElementCollection::iterator GetOldest(log_id_t log_id);
+
+    LogReaderList* reader_list_;
+    LogTags* tags_;
+    PruneList* prune_;
+    LogStatistics* stats_;
+
+    // Keeps track of the iterator to the oldest log message of a given log type, as an
+    // optimization when pruning logs.  Use GetOldest() to retrieve.
+    std::optional<LogBufferElementCollection::iterator> oldest_[LOG_ID_MAX];
+};
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 4044dc9..0ff19f8 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "CommandListener.h"
+
 #include <arpa/inet.h>
 #include <ctype.h>
 #include <dirent.h>
@@ -35,12 +37,11 @@
 #include <private/android_filesystem_config.h>
 #include <sysutils/SocketClient.h>
 
-#include "CommandListener.h"
-#include "LogCommand.h"
 #include "LogUtils.h"
 
-CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune)
-    : FrameworkListener(getLogSocket()), buf_(buf), tags_(tags), prune_(prune) {
+CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune,
+                                 LogStatistics* stats)
+    : FrameworkListener(getLogSocket()), buf_(buf), tags_(tags), prune_(prune), stats_(stats) {
     registerCmd(new ClearCmd(this));
     registerCmd(new GetBufSizeCmd(this));
     registerCmd(new SetBufSizeCmd(this));
@@ -80,7 +81,7 @@
         return 0;
     }
 
-    cli->sendMsg(buf()->clear((log_id_t)id, uid) ? "busy" : "success");
+    cli->sendMsg(buf()->Clear((log_id_t)id, uid) ? "busy" : "success");
     return 0;
 }
 
@@ -98,7 +99,7 @@
         return 0;
     }
 
-    unsigned long size = buf()->getSize((log_id_t)id);
+    unsigned long size = buf()->GetSize((log_id_t)id);
     char buf[512];
     snprintf(buf, sizeof(buf), "%lu", size);
     cli->sendMsg(buf);
@@ -125,7 +126,7 @@
     }
 
     unsigned long size = atol(argv[2]);
-    if (buf()->setSize((log_id_t)id, size)) {
+    if (buf()->SetSize((log_id_t)id, size)) {
         cli->sendMsg("Range Error");
         return 0;
     }
@@ -148,7 +149,7 @@
         return 0;
     }
 
-    unsigned long size = buf()->getSizeUsed((log_id_t)id);
+    unsigned long size = stats()->Sizes((log_id_t)id);
     char buf[512];
     snprintf(buf, sizeof(buf), "%lu", size);
     cli->sendMsg(buf);
@@ -209,7 +210,7 @@
         }
     }
 
-    cli->sendMsg(PackageString(buf()->formatStatistics(uid, pid, logMask)).c_str());
+    cli->sendMsg(PackageString(stats()->Format(uid, pid, logMask)).c_str());
     return 0;
 }
 
@@ -298,7 +299,7 @@
     setname();
 
     android::prdebug("logd reinit");
-    buf()->init();
+    buf()->Init();
     prune()->init(nullptr);
 
     // This only works on userdebug and eng devices to re-read the
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index c90c247..fd934f7 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -22,12 +22,13 @@
 #include "LogCommand.h"
 #include "LogListener.h"
 #include "LogReader.h"
+#include "LogStatistics.h"
 #include "LogTags.h"
 #include "LogWhiteBlackList.h"
 
 class CommandListener : public FrameworkListener {
   public:
-    CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune);
+    CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune, LogStatistics* log_statistics);
     virtual ~CommandListener() {}
 
   private:
@@ -36,20 +37,22 @@
     LogBuffer* buf_;
     LogTags* tags_;
     PruneList* prune_;
+    LogStatistics* stats_;
 
-#define LogCmd(name, command_string)                            \
-    class name##Cmd : public LogCommand {                       \
-      public:                                                   \
-        explicit name##Cmd(CommandListener* parent)             \
-            : LogCommand(#command_string), parent_(parent) {}   \
-        virtual ~name##Cmd() {}                                 \
-        int runCommand(SocketClient* c, int argc, char** argv); \
-                                                                \
-      private:                                                  \
-        LogBuffer* buf() const { return parent_->buf_; }        \
-        LogTags* tags() const { return parent_->tags_; }        \
-        PruneList* prune() const { return parent_->prune_; }    \
-        CommandListener* parent_;                               \
+#define LogCmd(name, command_string)                             \
+    class name##Cmd : public LogCommand {                        \
+      public:                                                    \
+        explicit name##Cmd(CommandListener* parent)              \
+            : LogCommand(#command_string), parent_(parent) {}    \
+        virtual ~name##Cmd() {}                                  \
+        int runCommand(SocketClient* c, int argc, char** argv);  \
+                                                                 \
+      private:                                                   \
+        LogBuffer* buf() const { return parent_->buf_; }         \
+        LogTags* tags() const { return parent_->tags_; }         \
+        PruneList* prune() const { return parent_->prune_; }     \
+        LogStatistics* stats() const { return parent_->stats_; } \
+        CommandListener* parent_;                                \
     }
 
     LogCmd(Clear, clear);
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index d9cc0db..0ce9796 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "LogAudit.h"
+
 #include <ctype.h>
 #include <endian.h>
 #include <errno.h>
@@ -34,10 +36,7 @@
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
-#include "LogAudit.h"
-#include "LogBuffer.h"
 #include "LogKlog.h"
-#include "LogReader.h"
 #include "LogUtils.h"
 #include "libaudit.h"
 
@@ -45,16 +44,14 @@
     '<', '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) / 10, \
         '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) % 10, '>'
 
-LogAudit::LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg)
+LogAudit::LogAudit(LogBuffer* buf, int fdDmesg, LogStatistics* stats)
     : SocketListener(getLogSocket(), false),
       logbuf(buf),
-      reader(reader),
       fdDmesg(fdDmesg),
-      main(__android_logger_property_get_bool("ro.logd.auditd.main",
-                                              BOOL_DEFAULT_TRUE)),
-      events(__android_logger_property_get_bool("ro.logd.auditd.events",
-                                                BOOL_DEFAULT_TRUE)),
-      initialized(false) {
+      main(__android_logger_property_get_bool("ro.logd.auditd.main", BOOL_DEFAULT_TRUE)),
+      events(__android_logger_property_get_bool("ro.logd.auditd.events", BOOL_DEFAULT_TRUE)),
+      initialized(false),
+      stats_(stats) {
     static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
                                            'l',
                                            'o',
@@ -211,9 +208,7 @@
             ++cp;
         }
         tid = pid;
-        logbuf->wrlock();
-        uid = logbuf->pidToUid(pid);
-        logbuf->unlock();
+        uid = stats_->PidToUid(pid);
         memmove(pidptr, cp, strlen(cp) + 1);
     }
 
@@ -247,22 +242,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 +260,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)];
@@ -291,9 +274,8 @@
         memcpy(event->data + str_len - denial_metadata.length(),
                denial_metadata.c_str(), denial_metadata.length());
 
-        rc = logbuf->log(
-            LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),
-            (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
+        rc = logbuf->Log(LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),
+                         (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
         if (rc >= 0) {
             notify |= 1 << LOG_ID_EVENTS;
         }
@@ -313,9 +295,7 @@
         pid = tid;
         comm = "auditd";
     } else {
-        logbuf->wrlock();
-        comm = commfree = logbuf->pidToName(pid);
-        logbuf->unlock();
+        comm = commfree = stats_->PidToName(pid);
         if (!comm) {
             comm = "unknown";
         }
@@ -347,9 +327,8 @@
         strncpy(newstr + 1 + str_len + prefix_len + suffix_len,
                 denial_metadata.c_str(), denial_metadata.length());
 
-        rc = logbuf->log(
-            LOG_ID_MAIN, now, uid, pid, tid, newstr,
-            (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
+        rc = logbuf->Log(LOG_ID_MAIN, now, uid, pid, tid, newstr,
+                         (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
 
         if (rc >= 0) {
             notify |= 1 << LOG_ID_MAIN;
@@ -361,7 +340,6 @@
     free(str);
 
     if (notify) {
-        reader->notifyNewLog(notify);
         if (rc < 0) {
             rc = message_len;
         }
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index c3d7a3e..181920e 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -14,36 +14,30 @@
  * limitations under the License.
  */
 
-#ifndef _LOGD_LOG_AUDIT_H__
-#define _LOGD_LOG_AUDIT_H__
+#pragma once
 
 #include <map>
 
 #include <sysutils/SocketListener.h>
 
 #include "LogBuffer.h"
-
-class LogReader;
+#include "LogStatistics.h"
 
 class LogAudit : public SocketListener {
     LogBuffer* logbuf;
-    LogReader* reader;
     int fdDmesg;  // fdDmesg >= 0 is functionally bool dmesg
     bool main;
     bool events;
     bool initialized;
 
-   public:
-    LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg);
+  public:
+    LogAudit(LogBuffer* buf, int fdDmesg, LogStatistics* stats);
     int log(char* buf, size_t len);
-    bool isMonotonic() {
-        return logbuf->isMonotonic();
-    }
 
-   protected:
+  protected:
     virtual bool onDataAvailable(SocketClient* cli);
 
-   private:
+  private:
     static int getLogSocket();
     std::map<std::string, std::string> populateDenialMap();
     std::string denialParse(const std::string& denial, char terminator,
@@ -51,6 +45,6 @@
     void auditParse(const std::string& string, uid_t uid, std::string* bug_num);
     int logPrint(const char* fmt, ...)
         __attribute__((__format__(__printf__, 2, 3)));
-};
 
-#endif
+    LogStatistics* stats_;
+};
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 9a36712..887e5f0 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2014 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,154 +18,37 @@
 
 #include <sys/types.h>
 
-#include <list>
-#include <optional>
-#include <string>
+#include <functional>
 
-#include <android/log.h>
-#include <private/android_filesystem_config.h>
+#include <log/log.h>
 #include <sysutils/SocketClient.h>
 
 #include "LogBufferElement.h"
-#include "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;
+enum class FlushToResult {
+    kSkip,
+    kStop,
+    kWrite,
+};
 
 class LogBuffer {
-    LogBufferElementCollection mLogElements;
-    pthread_rwlock_t mLogElementsLock;
+  public:
+    virtual ~LogBuffer() {}
 
-    LogStatistics stats;
+    virtual void Init() = 0;
 
-    // watermark of any worst/chatty uid processing
-    typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator>
-        LogBufferIteratorMap;
-    LogBufferIteratorMap mLastWorst[LOG_ID_MAX];
-    // watermark of any worst/chatty pid of system processing
-    typedef std::unordered_map<pid_t, LogBufferElementCollection::iterator>
-        LogBufferPidIteratorMap;
-    LogBufferPidIteratorMap mLastWorstPidOfSystem[LOG_ID_MAX];
-
-    unsigned long mMaxSize[LOG_ID_MAX];
-
-    bool monotonic;
-
-    LogBufferElement* lastLoggedElements[LOG_ID_MAX];
-    LogBufferElement* droppedElements[LOG_ID_MAX];
-    void log(LogBufferElement* elem);
-
-   public:
-    LastLogTimes& mTimes;
-
-    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);
+    virtual int Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
+                    const char* msg, uint16_t len) = 0;
     // lastTid is an optional context to help detect if the last previous
     // valid message was from the same source so we can differentiate chatty
     // filter types (identical or expired)
-    uint64_t flushTo(SocketClient* writer, uint64_t start,
-                     pid_t* lastTid,  // &lastTid[LOG_ID_MAX] or nullptr
-                     bool privileged, bool security,
-                     int (*filter)(const LogBufferElement* element, void* arg) = nullptr,
-                     void* arg = nullptr);
+    virtual uint64_t FlushTo(
+            SocketClient* writer, uint64_t start,
+            pid_t* last_tid,  // nullable
+            bool privileged, bool security,
+            const std::function<FlushToResult(const LogBufferElement* element)>& filter) = 0;
 
-    bool clear(log_id_t id, uid_t uid = AID_ROOT);
-    unsigned long getSize(log_id_t id);
-    int setSize(log_id_t id, unsigned long size);
-    unsigned long getSizeUsed(log_id_t id);
-
-    std::string formatStatistics(uid_t uid, pid_t pid, unsigned int logMask);
-
-    void enableStatistics() {
-        stats.enableStatistics();
-    }
-
-    // helper must be protected directly or implicitly by wrlock()/unlock()
-    const char* pidToName(pid_t pid) {
-        return stats.pidToName(pid);
-    }
-    uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
-    const char* uidToName(uid_t uid) {
-        return stats.uidToName(uid);
-    }
-    void wrlock() {
-        pthread_rwlock_wrlock(&mLogElementsLock);
-    }
-    void rdlock() {
-        pthread_rwlock_rdlock(&mLogElementsLock);
-    }
-    void unlock() {
-        pthread_rwlock_unlock(&mLogElementsLock);
-    }
-
-   private:
-    static constexpr size_t minPrune = 4;
-    static constexpr size_t maxPrune = 256;
-
-    void maybePrune(log_id_t id);
-    void kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows);
-
-    bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
-    LogBufferElementCollection::iterator erase(
-        LogBufferElementCollection::iterator it, bool coalesce = false);
-
-    // Returns an iterator to the oldest element for a given log type, or mLogElements.end() if
-    // there are no logs for the given log type. Requires mLogElementsLock to be held.
-    LogBufferElementCollection::iterator GetOldest(log_id_t log_id);
-
-    LogTags* tags_;
-    PruneList* prune_;
-
-    // Keeps track of the iterator to the oldest log message of a given log type, as an
-    // optimization when pruning logs.  Use GetOldest() to retrieve.
-    std::optional<LogBufferElementCollection::iterator> oldest_[LOG_ID_MAX];
-};
+    virtual bool Clear(log_id_t id, uid_t uid) = 0;
+    virtual unsigned long GetSize(log_id_t id) = 0;
+    virtual int SetSize(log_id_t id, unsigned long size) = 0;
+};
\ No newline at end of file
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 916ed42..32f641b 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "LogBufferElement.h"
+
 #include <ctype.h>
 #include <endian.h>
 #include <fcntl.h>
@@ -25,10 +27,9 @@
 #include <log/log_read.h>
 #include <private/android_logger.h>
 
-#include "LogBuffer.h"
-#include "LogBufferElement.h"
 #include "LogCommand.h"
 #include "LogReader.h"
+#include "LogStatistics.h"
 #include "LogUtils.h"
 
 const uint64_t LogBufferElement::FLUSH_ERROR(0);
@@ -153,7 +154,7 @@
 }
 
 // assumption: mMsg == NULL
-size_t LogBufferElement::populateDroppedMessage(char*& buffer, LogBuffer* parent,
+size_t LogBufferElement::populateDroppedMessage(char*& buffer, LogStatistics* stats,
                                                 bool lastSame) {
     static const char tag[] = "chatty";
 
@@ -163,17 +164,13 @@
     }
 
     static const char format_uid[] = "uid=%u%s%s %s %u line%s";
-    parent->wrlock();
-    const char* name = parent->uidToName(mUid);
-    parent->unlock();
+    const char* name = stats->UidToName(mUid);
     const char* commName = android::tidToName(mTid);
     if (!commName && (mTid != mPid)) {
         commName = android::tidToName(mPid);
     }
     if (!commName) {
-        parent->wrlock();
-        commName = parent->pidToName(mPid);
-        parent->unlock();
+        commName = stats->PidToName(mPid);
     }
     if (name && name[0] && commName && (name[0] == commName[0])) {
         size_t len = strlen(name + 1);
@@ -246,7 +243,7 @@
     return retval;
 }
 
-uint64_t LogBufferElement::flushTo(SocketClient* reader, LogBuffer* parent, bool lastSame) {
+uint64_t LogBufferElement::flushTo(SocketClient* reader, LogStatistics* stats, bool lastSame) {
     struct logger_entry entry = {};
 
     entry.hdr_size = sizeof(struct logger_entry);
@@ -264,7 +261,7 @@
     char* buffer = nullptr;
 
     if (mDropped) {
-        entry.len = populateDroppedMessage(buffer, parent, lastSame);
+        entry.len = populateDroppedMessage(buffer, stats, lastSame);
         if (!entry.len) return mSequence;
         iovec[1].iov_base = buffer;
     } else {
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 434b7db..3d0b65e 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -24,7 +24,7 @@
 #include <log/log.h>
 #include <sysutils/SocketClient.h>
 
-class LogBuffer;
+class LogStatistics;
 
 #define EXPIRE_HOUR_THRESHOLD 24  // Only expire chatty UID logs to preserve
                                   // non-chatty UIDs less than this age in hours
@@ -33,8 +33,6 @@
 #define EXPIRE_RATELIMIT 10  // maximum rate in seconds to report expiration
 
 class __attribute__((packed)) LogBufferElement {
-    friend LogBuffer;
-
     // sized to match reality of incoming log packets
     const uint32_t mUid;
     const uint32_t mPid;
@@ -55,10 +53,9 @@
     static atomic_int_fast64_t sequence;
 
     // assumption: mDropped == true
-    size_t populateDroppedMessage(char*& buffer, LogBuffer* parent,
-                                  bool lastSame);
+    size_t populateDroppedMessage(char*& buffer, LogStatistics* parent, bool lastSame);
 
-   public:
+  public:
     LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
                      pid_t tid, const char* msg, uint16_t len);
     LogBufferElement(const LogBufferElement& elem);
@@ -98,5 +95,5 @@
     }
 
     static const uint64_t FLUSH_ERROR;
-    uint64_t flushTo(SocketClient* writer, LogBuffer* parent, bool lastSame);
+    uint64_t flushTo(SocketClient* writer, LogStatistics* parent, bool lastSame);
 };
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index edd326a..1ea87a9 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "LogKlog.h"
+
 #include <ctype.h>
 #include <errno.h>
 #include <inttypes.h>
@@ -29,8 +31,6 @@
 #include <private/android_logger.h>
 
 #include "LogBuffer.h"
-#include "LogKlog.h"
-#include "LogReader.h"
 
 #define KMSG_PRIORITY(PRI) \
     '<', '0' + (LOG_SYSLOG | (PRI)) / 10, '0' + (LOG_SYSLOG | (PRI)) % 10, '>'
@@ -201,15 +201,14 @@
                                        ? log_time(log_time::EPOCH)
                                        : (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
 
-LogKlog::LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
-                 bool auditd)
+LogKlog::LogKlog(LogBuffer* buf, int fdWrite, int fdRead, bool auditd, LogStatistics* stats)
     : SocketListener(fdRead, false),
       logbuf(buf),
-      reader(reader),
       signature(CLOCK_MONOTONIC),
       initialized(false),
       enableLogging(true),
-      auditd(auditd) {
+      auditd(auditd),
+      stats_(stats) {
     static const char klogd_message[] = "%s%s%" PRIu64 "\n";
     char buffer[strlen(priority_message) + strlen(klogdStr) +
                 strlen(klogd_message) + 20];
@@ -309,8 +308,6 @@
         }
         buf = cp;
 
-        if (isMonotonic()) return now;
-
         const char* b;
         if (((b = android::strnstr(cp, len, suspendStr))) &&
             (((b += strlen(suspendStr)) - cp) < len)) {
@@ -356,11 +353,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 +424,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) {
@@ -573,9 +527,7 @@
     const pid_t tid = pid;
     uid_t uid = AID_ROOT;
     if (pid) {
-        logbuf->wrlock();
-        uid = logbuf->pidToUid(pid);
-        logbuf->unlock();
+        uid = stats_->PidToUid(pid);
     }
 
     // Parse (rules at top) to pull out a tag from the incoming kernel message.
@@ -789,7 +741,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
@@ -815,12 +767,7 @@
     }
 
     // Log message
-    int rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, (uint16_t)n);
-
-    // notify readers
-    if (rc > 0) {
-        reader->notifyNewLog(static_cast<log_mask_t>(1 << LOG_ID_KERNEL));
-    }
+    int rc = logbuf->Log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, (uint16_t)n);
 
     return rc;
 }
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index 6bfd6a8..77b24bc 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -14,18 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef _LOGD_LOG_KLOG_H__
-#define _LOGD_LOG_KLOG_H__
+#pragma once
 
 #include <private/android_logger.h>
 #include <sysutils/SocketListener.h>
 
+#include "LogStatistics.h"
+
 class LogBuffer;
-class LogReader;
 
 class LogKlog : public SocketListener {
     LogBuffer* logbuf;
-    LogReader* reader;
     const log_time signature;
     // Set once thread is started, separates KLOG_ACTION_READ_ALL
     // and KLOG_ACTION_READ phases.
@@ -38,27 +37,18 @@
 
     static log_time correction;
 
-   public:
-    LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
-            bool auditd);
+  public:
+    LogKlog(LogBuffer* buf, int fdWrite, int fdRead, bool auditd, LogStatistics* stats);
     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;
-    }
+    static void convertMonotonicToReal(log_time& real) { real += correction; }
 
-   protected:
-     log_time sniffTime(const char*& buf, ssize_t len, bool reverse);
-     pid_t sniffPid(const char*& buf, ssize_t len);
-     void calculateCorrection(const log_time& monotonic, const char* real_string, ssize_t len);
-     virtual bool onDataAvailable(SocketClient* cli);
+  protected:
+    log_time sniffTime(const char*& buf, ssize_t len, bool reverse);
+    pid_t sniffPid(const char*& buf, ssize_t len);
+    void calculateCorrection(const log_time& monotonic, const char* real_string, ssize_t len);
+    virtual bool onDataAvailable(SocketClient* cli);
+
+  private:
+    LogStatistics* stats_;
 };
-
-#endif
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index ba61042..d2e2efa 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>
@@ -30,34 +32,43 @@
 #include "LogListener.h"
 #include "LogUtils.h"
 
-LogListener::LogListener(LogBuffer* buf, LogReader* reader)
-    : SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {}
+LogListener::LogListener(LogBuffer* buf) : socket_(GetLogSocket()), logbuf_(buf) {}
 
-bool LogListener::onDataAvailable(SocketClient* cli) {
-    static bool name_set;
-    if (!name_set) {
-        prctl(PR_SET_NAME, "logd.writer");
-        name_set = true;
+bool LogListener::StartListener() {
+    if (socket_ <= 0) {
+        return false;
     }
+    auto thread = std::thread(&LogListener::ThreadFunction, this);
+    thread.detach();
+    return true;
+}
 
+void LogListener::ThreadFunction() {
+    prctl(PR_SET_NAME, "logd.writer");
+
+    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 +86,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 +101,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 +116,11 @@
     // NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
     // truncated message to the logs.
 
-    int res = logbuf->log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
-                          ((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
-    if (res > 0) {
-        reader->notifyNewLog(static_cast<log_mask_t>(1 << logId));
-    }
-
-    return true;
+    logbuf_->Log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
+                 ((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
 }
 
-int LogListener::getLogSocket() {
+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..d468df8 100644
--- a/logd/LogListener.h
+++ b/logd/LogListener.h
@@ -14,24 +14,21 @@
  * 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);
+    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_;
 };
-
-#endif
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index c6dea69..6f91372 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -21,7 +21,10 @@
 #include <sys/socket.h>
 #include <sys/types.h>
 
+#include <chrono>
+
 #include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
 #include "LogBuffer.h"
@@ -33,27 +36,8 @@
     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 log_mask) {
-    LastLogTimes& times = mLogbuf.mTimes;
-
-    LogTimeEntry::wrlock();
-    for (const auto& entry : times) {
-        if (!entry->isWatchingMultiple(log_mask)) {
-            continue;
-        }
-        if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) {
-            continue;
-        }
-        entry->triggerReader_Locked();
-    }
-    LogTimeEntry::unlock();
-}
+LogReader::LogReader(LogBuffer* logbuf, LogReaderList* reader_list)
+    : SocketListener(getLogSocket(), true), log_buffer_(logbuf), reader_list_(reader_list) {}
 
 // Note returning false will release the SocketClient instance.
 bool LogReader::onDataAvailable(SocketClient* cli) {
@@ -74,15 +58,15 @@
 
     // Clients are only allowed to send one command, disconnect them if they
     // send another.
-    LogTimeEntry::wrlock();
-    for (const auto& entry : mLogbuf.mTimes) {
-        if (entry->mClient == cli) {
-            entry->release_Locked();
-            LogTimeEntry::unlock();
-            return false;
+    {
+        auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
+        for (const auto& entry : reader_list_->reader_threads()) {
+            if (entry->client() == cli) {
+                entry->release_Locked();
+                return false;
+            }
         }
     }
-    LogTimeEntry::unlock();
 
     unsigned long tail = 0;
     static const char _tail[] = " tail=";
@@ -99,11 +83,12 @@
         start.strptime(cp + sizeof(_start) - 1, "%s.%q");
     }
 
-    uint64_t timeout = 0;
+    std::chrono::steady_clock::time_point deadline = {};
     static const char _timeout[] = " timeout=";
     cp = strstr(buffer, _timeout);
     if (cp) {
-        timeout = atol(cp + sizeof(_timeout) - 1) * NS_PER_SEC + log_time(CLOCK_MONOTONIC).nsec();
+        long timeout_seconds = atol(cp + sizeof(_timeout) - 1);
+        deadline = std::chrono::steady_clock::now() + std::chrono::seconds(timeout_seconds);
     }
 
     unsigned int logMask = -1;
@@ -137,8 +122,8 @@
     if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) {
         // Allow writer to get some cycles, and wait for pending notifications
         sched_yield();
-        LogTimeEntry::wrlock();
-        LogTimeEntry::unlock();
+        reader_list_->reader_threads_lock().lock();
+        reader_list_->reader_threads_lock().unlock();
         sched_yield();
         nonBlock = true;
     }
@@ -149,56 +134,34 @@
     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) -> FlushToResult {
+            if (pid && pid != element->getPid()) {
+                return FlushToResult::kSkip;
             }
+            if ((logMask & (1 << element->getLogId())) == 0) {
+                return FlushToResult::kSkip;
+            }
+            if (start == element->getRealTime()) {
+                sequence = element->getSequence();
+                start_time_set = true;
+                return FlushToResult::kStop;
+            } else {
+                if (start < element->getRealTime()) {
+                    sequence = last;
+                    start_time_set = true;
+                    return FlushToResult::kStop;
+                }
+                last = element->getSequence();
+            }
+            return FlushToResult::kSkip;
+        };
 
-            bool found() { return startTimeSet; }
-        } logFindStart(logMask, pid, start, sequence,
-                       logbuf().isMonotonic() && android::isMonotonic(start));
+        log_buffer_->FlushTo(cli, sequence, nullptr, privileged, can_read_security, log_find_start);
 
-        logbuf().flushTo(cli, sequence, nullptr, privileged, can_read_security,
-                         logFindStart.callback, &logFindStart);
-
-        if (!logFindStart.found()) {
+        if (!start_time_set) {
             if (nonBlock) {
                 doSocketDelete(cli);
                 return false;
@@ -209,49 +172,45 @@
 
     android::prdebug(
             "logdr: UID=%d GID=%d PID=%d %c tail=%lu logMask=%x pid=%d "
-            "start=%" PRIu64 "ns timeout=%" PRIu64 "ns\n",
+            "start=%" PRIu64 "ns deadline=%" PRIi64 "ns\n",
             cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail, logMask,
-            (int)pid, start.nsec(), timeout);
+            (int)pid, start.nsec(), static_cast<int64_t>(deadline.time_since_epoch().count()));
 
     if (start == log_time::EPOCH) {
-        timeout = 0;
+        deadline = {};
     }
 
-    LogTimeEntry::wrlock();
-    auto entry = std::make_unique<LogTimeEntry>(*this, cli, nonBlock, tail, logMask, pid, start,
-                                                sequence, timeout, privileged, can_read_security);
+    auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
+    auto entry = std::make_unique<LogReaderThread>(*this, *reader_list_, cli, nonBlock, tail,
+                                                   logMask, pid, start, sequence, deadline,
+                                                   privileged, can_read_security);
     if (!entry->startReader_Locked()) {
-        LogTimeEntry::unlock();
         return false;
     }
 
     // release client and entry reference counts once done
     cli->incRef();
-    mLogbuf.mTimes.emplace_front(std::move(entry));
+    reader_list_->reader_threads().emplace_front(std::move(entry));
 
     // Set acceptable upper limit to wait for slow reader processing b/27242723
     struct timeval t = { LOGD_SNDTIMEO, 0 };
     setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char*)&t,
                sizeof(t));
 
-    LogTimeEntry::unlock();
-
     return true;
 }
 
 void LogReader::doSocketDelete(SocketClient* cli) {
-    LastLogTimes& times = mLogbuf.mTimes;
-    LogTimeEntry::wrlock();
-    LastLogTimes::iterator it = times.begin();
-    while (it != times.end()) {
-        LogTimeEntry* entry = it->get();
-        if (entry->mClient == cli) {
+    auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
+    auto it = reader_list_->reader_threads().begin();
+    while (it != reader_list_->reader_threads().end()) {
+        LogReaderThread* entry = it->get();
+        if (entry->client() == cli) {
             entry->release_Locked();
             break;
         }
         it++;
     }
-    LogTimeEntry::unlock();
 }
 
 int LogReader::getLogSocket() {
diff --git a/logd/LogReader.h b/logd/LogReader.h
index b5312b6..7df3f6b 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -14,35 +14,31 @@
  * limitations under the License.
  */
 
-#ifndef _LOGD_LOG_WRITER_H__
-#define _LOGD_LOG_WRITER_H__
+#pragma once
 
 #include <sysutils/SocketListener.h>
 
-#include "LogTimes.h"
+#include "LogReaderList.h"
+#include "LogReaderThread.h"
 
 #define LOGD_SNDTIMEO 32
 
 class LogBuffer;
 
 class LogReader : public SocketListener {
-    LogBuffer& mLogbuf;
+  public:
+    explicit LogReader(LogBuffer* logbuf, LogReaderList* reader_list);
 
-   public:
-    explicit LogReader(LogBuffer* logbuf);
-    void notifyNewLog(log_mask_t logMask);
+    LogBuffer* log_buffer() const { return log_buffer_; }
 
-    LogBuffer& logbuf(void) const {
-        return mLogbuf;
-    }
-
-   protected:
+  protected:
     virtual bool onDataAvailable(SocketClient* cli);
 
-   private:
+  private:
     static int getLogSocket();
 
     void doSocketDelete(SocketClient* cli);
-};
 
-#endif
+    LogBuffer* log_buffer_;
+    LogReaderList* reader_list_;
+};
diff --git a/logd/LogReaderList.cpp b/logd/LogReaderList.cpp
new file mode 100644
index 0000000..220027b
--- /dev/null
+++ b/logd/LogReaderList.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LogReaderList.h"
+
+// When we are notified a new log entry is available, inform
+// listening sockets who are watching this entry's log id.
+void LogReaderList::NotifyNewLog(unsigned int log_mask) const {
+    auto lock = std::lock_guard{reader_threads_lock_};
+
+    for (const auto& entry : reader_threads_) {
+        if (!entry->IsWatchingMultiple(log_mask)) {
+            continue;
+        }
+        if (entry->deadline().time_since_epoch().count() != 0) {
+            continue;
+        }
+        entry->triggerReader_Locked();
+    }
+}
diff --git a/logd/LogReaderList.h b/logd/LogReaderList.h
new file mode 100644
index 0000000..0d84aba
--- /dev/null
+++ b/logd/LogReaderList.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+#include <mutex>
+
+#include "LogReaderThread.h"
+
+class LogReaderList {
+  public:
+    void NotifyNewLog(unsigned int log_mask) const;
+
+    std::list<std::unique_ptr<LogReaderThread>>& reader_threads() { return reader_threads_; }
+    std::mutex& reader_threads_lock() { return reader_threads_lock_; }
+
+  private:
+    std::list<std::unique_ptr<LogReaderThread>> reader_threads_;
+    mutable std::mutex reader_threads_lock_;
+};
\ No newline at end of file
diff --git a/logd/LogReaderThread.cpp b/logd/LogReaderThread.cpp
new file mode 100644
index 0000000..e58e3eb
--- /dev/null
+++ b/logd/LogReaderThread.cpp
@@ -0,0 +1,221 @@
+/*
+ * 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;
+
+LogReaderThread::LogReaderThread(LogReader& reader, LogReaderList& reader_list,
+                                 SocketClient* client, bool non_block, unsigned long tail,
+                                 unsigned int log_mask, pid_t pid, log_time start_time,
+                                 uint64_t start, std::chrono::steady_clock::time_point deadline,
+                                 bool privileged, bool can_read_security_logs)
+    : leading_dropped_(false),
+      reader_(reader),
+      reader_list_(reader_list),
+      log_mask_(log_mask),
+      pid_(pid),
+      tail_(tail),
+      count_(0),
+      index_(0),
+      client_(client),
+      start_time_(start_time),
+      start_(start),
+      deadline_(deadline),
+      non_block_(non_block),
+      privileged_(privileged),
+      can_read_security_logs_(can_read_security_logs) {
+    memset(last_tid_, 0, sizeof(last_tid_));
+    cleanSkip_Locked();
+}
+
+bool LogReaderThread::startReader_Locked() {
+    auto thread = std::thread{&LogReaderThread::ThreadFunction, this};
+    thread.detach();
+    return true;
+}
+
+void LogReaderThread::ThreadFunction() {
+    prctl(PR_SET_NAME, "logd.reader.per");
+
+    SocketClient* client = client_;
+
+    LogBuffer& logbuf = *reader_.log_buffer();
+
+    leading_dropped_ = true;
+
+    auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+
+    uint64_t start = start_;
+
+    while (!release_) {
+        if (deadline_.time_since_epoch().count() != 0) {
+            if (thread_triggered_condition_.wait_until(lock, deadline_) ==
+                std::cv_status::timeout) {
+                deadline_ = {};
+            }
+            if (release_) {
+                break;
+            }
+        }
+
+        lock.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;
+
+        lock.lock();
+
+        if (start == LogBufferElement::FLUSH_ERROR) {
+            break;
+        }
+
+        start_ = start + 1;
+
+        if (non_block_ || release_) {
+            break;
+        }
+
+        cleanSkip_Locked();
+
+        if (deadline_.time_since_epoch().count() == 0) {
+            thread_triggered_condition_.wait(lock);
+        }
+    }
+
+    reader_.release(client);
+    client->decRef();
+
+    auto& log_reader_threads = reader_list_.reader_threads();
+    auto it = std::find_if(log_reader_threads.begin(), log_reader_threads.end(),
+                           [this](const auto& other) { return other.get() == this; });
+
+    if (it != log_reader_threads.end()) {
+        log_reader_threads.erase(it);
+    }
+}
+
+// A first pass to count the number of elements
+FlushToResult LogReaderThread::FilterFirstPass(const LogBufferElement* element) {
+    auto lock = std::lock_guard{reader_list_.reader_threads_lock()};
+
+    if (leading_dropped_) {
+        if (element->getDropped()) {
+            return FlushToResult::kSkip;
+        }
+        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_;
+    }
+
+    return FlushToResult::kSkip;
+}
+
+// A second pass to send the selected elements
+FlushToResult LogReaderThread::FilterSecondPass(const LogBufferElement* element) {
+    auto lock = std::lock_guard{reader_list_.reader_threads_lock()};
+
+    start_ = element->getSequence();
+
+    if (skip_ahead_[element->getLogId()]) {
+        skip_ahead_[element->getLogId()]--;
+        return FlushToResult::kSkip;
+    }
+
+    if (leading_dropped_) {
+        if (element->getDropped()) {
+            return FlushToResult::kSkip;
+        }
+        leading_dropped_ = false;
+    }
+
+    // Truncate to close race between first and second pass
+    if (non_block_ && tail_ && index_ >= count_) {
+        return FlushToResult::kStop;
+    }
+
+    if (!IsWatching(element->getLogId())) {
+        return FlushToResult::kSkip;
+    }
+
+    if (pid_ && pid_ != element->getPid()) {
+        return FlushToResult::kSkip;
+    }
+
+    if (start_time_ != log_time::EPOCH && element->getRealTime() <= start_time_) {
+        return FlushToResult::kSkip;
+    }
+
+    if (release_) {
+        return FlushToResult::kStop;
+    }
+
+    if (!tail_) {
+        goto ok;
+    }
+
+    ++index_;
+
+    if (count_ > tail_ && index_ <= (count_ - tail_)) {
+        return FlushToResult::kSkip;
+    }
+
+    if (!non_block_) {
+        tail_ = 0;
+    }
+
+ok:
+    if (!skip_ahead_[element->getLogId()]) {
+        return FlushToResult::kWrite;
+    }
+    return FlushToResult::kSkip;
+}
+
+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..f828b6e
--- /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 <chrono>
+#include <condition_variable>
+#include <list>
+#include <memory>
+
+#include <log/log.h>
+#include <sysutils/SocketClient.h>
+
+#include "LogBuffer.h"
+
+class LogReader;
+class LogBufferElement;
+class LogReaderList;
+
+class LogReaderThread {
+  public:
+    LogReaderThread(LogReader& reader, LogReaderList& reader_list, SocketClient* client,
+                    bool non_block, unsigned long tail, unsigned int log_mask, pid_t pid,
+                    log_time start_time, uint64_t sequence,
+                    std::chrono::steady_clock::time_point deadline, bool privileged,
+                    bool can_read_security_logs);
+
+    bool startReader_Locked();
+
+    void triggerReader_Locked() { thread_triggered_condition_.notify_all(); }
+
+    void triggerSkip_Locked(log_id_t id, unsigned int skip) { skip_ahead_[id] = skip; }
+    void cleanSkip_Locked();
+
+    void release_Locked() {
+        // gracefully shut down the socket.
+        shutdown(client_->getSocket(), SHUT_RDWR);
+        release_ = true;
+        thread_triggered_condition_.notify_all();
+    }
+
+    bool IsWatching(log_id_t id) const { return log_mask_ & (1 << id); }
+    bool IsWatchingMultiple(unsigned int log_mask) const { return log_mask_ & log_mask; }
+
+    const SocketClient* client() const { return client_; }
+    uint64_t start() const { return start_; }
+    std::chrono::steady_clock::time_point deadline() const { return deadline_; }
+
+  private:
+    void ThreadFunction();
+    // flushTo filter callbacks
+    FlushToResult FilterFirstPass(const LogBufferElement* element);
+    FlushToResult 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.
+    std::condition_variable thread_triggered_condition_;
+
+    // Reference to the parent thread that manages log reader sockets.
+    LogReader& reader_;
+    // Reference to the parent list that shares its lock with each instance
+    LogReaderList& reader_list_;
+    // A mask of the logs buffers that are read by this reader.
+    const unsigned int log_mask_;
+    // If set to non-zero, only pids equal to this are read by the reader.
+    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 deadline used for log wrapping.  If this deadline expires before logs
+    // wrap, then wake up and send the logs to the reader anyway.
+    std::chrono::steady_clock::time_point deadline_;
+    // If this reader is 'dumpAndClose' and will disconnect once it has read its intended logs.
+    const bool non_block_;
+
+    // Whether or not this reader can read logs from all UIDs or only its own UID.  See
+    // clientHasLogCredentials().
+    bool privileged_;
+    // Whether or not this reader can read security logs.  See CanReadSecurityLogs().
+    bool can_read_security_logs_;
+};
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 431b778..14bcb63 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "LogStatistics.h"
+
 #include <ctype.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -27,14 +29,12 @@
 
 #include <private/android_logger.h>
 
-#include "LogStatistics.h"
-
 static const uint64_t hourSec = 60 * 60;
 static const uint64_t monthSec = 31 * 24 * hourSec;
 
-size_t LogStatistics::SizesTotal;
+std::atomic<size_t> LogStatistics::SizesTotal;
 
-LogStatistics::LogStatistics() : enable(false) {
+LogStatistics::LogStatistics(bool enable_statistics) : enable(enable_statistics) {
     log_time now(CLOCK_REALTIME);
     log_id_for_each(id) {
         mSizes[id] = 0;
@@ -79,7 +79,8 @@
 }
 }
 
-void LogStatistics::addTotal(LogBufferElement* element) {
+void LogStatistics::AddTotal(LogBufferElement* element) {
+    auto lock = std::lock_guard{lock_};
     if (element->getDropped()) return;
 
     log_id_t log_id = element->getLogId();
@@ -89,7 +90,8 @@
     ++mElementsTotal[log_id];
 }
 
-void LogStatistics::add(LogBufferElement* element) {
+void LogStatistics::Add(LogBufferElement* element) {
+    auto lock = std::lock_guard{lock_};
     log_id_t log_id = element->getLogId();
     uint16_t size = element->getMsgLen();
     mSizes[log_id] += size;
@@ -159,7 +161,8 @@
     }
 }
 
-void LogStatistics::subtract(LogBufferElement* element) {
+void LogStatistics::Subtract(LogBufferElement* element) {
+    auto lock = std::lock_guard{lock_};
     log_id_t log_id = element->getLogId();
     uint16_t size = element->getMsgLen();
     mSizes[log_id] -= size;
@@ -204,7 +207,8 @@
 
 // Atomically set an entry to drop
 // entry->setDropped(1) must follow this call, caller should do this explicitly.
-void LogStatistics::drop(LogBufferElement* element) {
+void LogStatistics::Drop(LogBufferElement* element) {
+    auto lock = std::lock_guard{lock_};
     log_id_t log_id = element->getLogId();
     uint16_t size = element->getMsgLen();
     mSizes[log_id] -= size;
@@ -238,9 +242,13 @@
     tagNameTable.subtract(TagNameKey(element), element);
 }
 
+const char* LogStatistics::UidToName(uid_t uid) const {
+    auto lock = std::lock_guard{lock_};
+    return UidToNameLocked(uid);
+}
+
 // caller must own and free character string
-// Requires parent LogBuffer::wrlock() to be held
-const char* LogStatistics::uidToName(uid_t uid) const {
+const char* LogStatistics::UidToNameLocked(uid_t uid) const {
     // Local hard coded favourites
     if (uid == AID_LOGD) {
         return strdup("auditd");
@@ -297,6 +305,80 @@
     return name;
 }
 
+template <typename TKey, typename TEntry>
+void LogStatistics::WorstTwoWithThreshold(const LogHashtable<TKey, TEntry>& table, size_t threshold,
+                                          int* worst, size_t* worst_sizes,
+                                          size_t* second_worst_sizes) const {
+    std::array<const TEntry*, 2> max_entries;
+    table.MaxEntries(AID_ROOT, 0, &max_entries);
+    if (max_entries[0] == nullptr || max_entries[1] == nullptr) {
+        return;
+    }
+    *worst_sizes = max_entries[0]->getSizes();
+    // b/24782000: Allow time horizon to extend roughly tenfold, assume average entry length is
+    // 100 characters.
+    if (*worst_sizes > threshold && *worst_sizes > (10 * max_entries[0]->getDropped())) {
+        *worst = max_entries[0]->getKey();
+        *second_worst_sizes = max_entries[1]->getSizes();
+        if (*second_worst_sizes < threshold) {
+            *second_worst_sizes = threshold;
+        }
+    }
+}
+
+void LogStatistics::WorstTwoUids(log_id id, size_t threshold, int* worst, size_t* worst_sizes,
+                                 size_t* second_worst_sizes) const {
+    auto lock = std::lock_guard{lock_};
+    WorstTwoWithThreshold(uidTable[id], threshold, worst, worst_sizes, second_worst_sizes);
+}
+
+void LogStatistics::WorstTwoTags(size_t threshold, int* worst, size_t* worst_sizes,
+                                 size_t* second_worst_sizes) const {
+    auto lock = std::lock_guard{lock_};
+    WorstTwoWithThreshold(tagTable, threshold, worst, worst_sizes, second_worst_sizes);
+}
+
+void LogStatistics::WorstTwoSystemPids(log_id id, size_t worst_uid_sizes, int* worst,
+                                       size_t* second_worst_sizes) const {
+    auto lock = std::lock_guard{lock_};
+    std::array<const PidEntry*, 2> max_entries;
+    pidSystemTable[id].MaxEntries(AID_SYSTEM, 0, &max_entries);
+    if (max_entries[0] == nullptr || max_entries[1] == nullptr) {
+        return;
+    }
+
+    *worst = max_entries[0]->getKey();
+    *second_worst_sizes = worst_uid_sizes - max_entries[0]->getSizes() + max_entries[1]->getSizes();
+}
+
+// Prune at most 10% of the log entries or maxPrune, whichever is less.
+bool LogStatistics::ShouldPrune(log_id id, unsigned long max_size,
+                                unsigned long* prune_rows) const {
+    static constexpr size_t kMinPrune = 4;
+    static constexpr size_t kMaxPrune = 256;
+
+    auto lock = std::lock_guard{lock_};
+    size_t sizes = mSizes[id];
+    if (sizes <= max_size) {
+        return false;
+    }
+    size_t size_over = sizes - ((max_size * 9) / 10);
+    size_t elements = mElements[id] - mDroppedElements[id];
+    size_t min_elements = elements / 100;
+    if (min_elements < kMinPrune) {
+        min_elements = kMinPrune;
+    }
+    *prune_rows = elements * size_over / sizes;
+    if (*prune_rows < min_elements) {
+        *prune_rows = min_elements;
+    }
+    if (*prune_rows > kMaxPrune) {
+        *prune_rows = kMaxPrune;
+    }
+
+    return true;
+}
+
 std::string UidEntry::formatHeader(const std::string& name, log_id_t id) const {
     bool isprune = worstUidEnabledForLogid(id);
     return formatLine(android::base::StringPrintf(name.c_str(),
@@ -308,10 +390,10 @@
 }
 
 // Helper to truncate name, if too long, and add name dressings
-static void formatTmp(const LogStatistics& stat, const char* nameTmp, uid_t uid,
-                      std::string& name, std::string& size, size_t nameLen) {
+void LogStatistics::FormatTmp(const char* nameTmp, uid_t uid, std::string& name, std::string& size,
+                              size_t nameLen) const {
     const char* allocNameTmp = nullptr;
-    if (!nameTmp) nameTmp = allocNameTmp = stat.uidToName(uid);
+    if (!nameTmp) nameTmp = allocNameTmp = UidToNameLocked(uid);
     if (nameTmp) {
         size_t lenSpace = std::max(nameLen - name.length(), (size_t)1);
         size_t len = EntryBaseConstants::total_len -
@@ -332,12 +414,12 @@
     }
 }
 
-std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const {
+std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const REQUIRES(stat.lock_) {
     uid_t uid = getUid();
     std::string name = android::base::StringPrintf("%u", uid);
     std::string size = android::base::StringPrintf("%zu", getSizes());
 
-    formatTmp(stat, nullptr, uid, name, size, 6);
+    stat.FormatTmp(nullptr, uid, name, size, 6);
 
     std::string pruned = "";
     if (worstUidEnabledForLogid(id)) {
@@ -347,9 +429,9 @@
              it != stat.uidTable[id].end(); ++it) {
             totalDropped += it->second.getDropped();
         }
-        size_t sizes = stat.sizes(id);
-        size_t totalSize = stat.sizesTotal(id);
-        size_t totalElements = stat.elementsTotal(id);
+        size_t sizes = stat.mSizes[id];
+        size_t totalSize = stat.mSizesTotal[id];
+        size_t totalElements = stat.mElementsTotal[id];
         float totalVirtualSize =
             (float)sizes + (float)totalDropped * totalSize / totalElements;
         size_t entrySize = getSizes();
@@ -401,12 +483,9 @@
     }
 
     static const size_t maximum_sorted_entries = 32;
-    std::unique_ptr<const PidEntry* []> sorted =
-        stat.pidSystemTable[id].sort(uid, (pid_t)0, maximum_sorted_entries);
+    std::array<const PidEntry*, maximum_sorted_entries> sorted;
+    stat.pidSystemTable[id].MaxEntries(uid, 0, &sorted);
 
-    if (!sorted.get()) {
-        return output;
-    }
     std::string byPid;
     size_t index;
     bool hasDropped = false;
@@ -440,14 +519,14 @@
                       std::string("BYTES"), std::string("NUM"));
 }
 
-std::string PidEntry::format(const LogStatistics& stat,
-                             log_id_t /* id */) const {
+std::string PidEntry::format(const LogStatistics& stat, log_id_t /* id */) const
+        REQUIRES(stat.lock_) {
     uid_t uid = getUid();
     pid_t pid = getPid();
     std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
     std::string size = android::base::StringPrintf("%zu", getSizes());
 
-    formatTmp(stat, getName(), uid, name, size, 12);
+    stat.FormatTmp(getName(), uid, name, size, 12);
 
     std::string pruned = "";
     size_t dropped = getDropped();
@@ -465,13 +544,13 @@
                       std::string("NUM"));
 }
 
-std::string TidEntry::format(const LogStatistics& stat,
-                             log_id_t /* id */) const {
+std::string TidEntry::format(const LogStatistics& stat, log_id_t /* id */) const
+        REQUIRES(stat.lock_) {
     uid_t uid = getUid();
     std::string name = android::base::StringPrintf("%5u/%u", getTid(), uid);
     std::string size = android::base::StringPrintf("%zu", getSizes());
 
-    formatTmp(stat, getName(), uid, name, size, 12);
+    stat.FormatTmp(getName(), uid, name, size, 12);
 
     std::string pruned = "";
     size_t dropped = getDropped();
@@ -611,8 +690,36 @@
     return output;
 }
 
-std::string LogStatistics::format(uid_t uid, pid_t pid,
-                                  unsigned int logMask) const {
+template <typename TKey, typename TEntry>
+std::string LogStatistics::FormatTable(const LogHashtable<TKey, TEntry>& table, uid_t uid,
+                                       pid_t pid, const std::string& name, log_id_t id) const
+        REQUIRES(lock_) {
+    static const size_t maximum_sorted_entries = 32;
+    std::string output;
+    std::array<const TEntry*, maximum_sorted_entries> sorted;
+    table.MaxEntries(uid, pid, &sorted);
+    bool header_printed = false;
+    for (size_t index = 0; index < maximum_sorted_entries; ++index) {
+        const TEntry* entry = sorted[index];
+        if (!entry) {
+            break;
+        }
+        if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
+            break;
+        }
+        if (!header_printed) {
+            output += "\n\n";
+            output += entry->formatHeader(name, id);
+            header_printed = true;
+        }
+        output += entry->format(*this, id);
+    }
+    return output;
+}
+
+std::string LogStatistics::Format(uid_t uid, pid_t pid, unsigned int logMask) const {
+    auto lock = std::lock_guard{lock_};
+
     static const uint16_t spaces_total = 19;
 
     // Report on total logging, current and for all time
@@ -642,9 +749,9 @@
         if (!(logMask & (1 << id))) continue;
         oldLength = output.length();
         if (spaces < 0) spaces = 0;
-        size_t szs = sizesTotal(id);
+        size_t szs = mSizesTotal[id];
         totalSize += szs;
-        size_t els = elementsTotal(id);
+        size_t els = mElementsTotal[id];
         totalEls += els;
         output +=
             android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els);
@@ -663,11 +770,11 @@
     log_id_for_each(id) {
         if (!(logMask & (1 << id))) continue;
 
-        size_t els = elements(id);
+        size_t els = mElements[id];
         if (els) {
             oldLength = output.length();
             if (spaces < 0) spaces = 0;
-            size_t szs = sizes(id);
+            size_t szs = mSizes[id];
             totalSize += szs;
             totalEls += els;
             output +=
@@ -749,7 +856,7 @@
     log_id_for_each(id) {
         if (!(logMask & (1 << id))) continue;
 
-        size_t els = elements(id);
+        size_t els = mElements[id];
         if (els) {
             oldLength = output.length();
             if (spaces < 0) spaces = 0;
@@ -758,7 +865,7 @@
                 ((sizeof(LogBufferElement) + sizeof(uint64_t) - 1) &
                  -sizeof(uint64_t)) +
                 sizeof(std::list<LogBufferElement*>);
-            size_t szs = sizes(id) + els * overhead;
+            size_t szs = mSizes[id] + els * overhead;
             totalSize += szs;
             output += android::base::StringPrintf("%*s%zu", spaces, "", szs);
             spaces -= output.length() - oldLength;
@@ -779,39 +886,38 @@
 
         name = (uid == AID_ROOT) ? "Chattiest UIDs in %s log buffer:"
                                  : "Logging for your UID in %s log buffer:";
-        output += uidTable[id].format(*this, uid, pid, name, id);
+        output += FormatTable(uidTable[id], uid, pid, name, id);
     }
 
     if (enable) {
         name = ((uid == AID_ROOT) && !pid) ? "Chattiest PIDs:"
                                            : "Logging for this PID:";
-        output += pidTable.format(*this, uid, pid, name);
+        output += FormatTable(pidTable, uid, pid, name);
         name = "Chattiest TIDs";
         if (pid) name += android::base::StringPrintf(" for PID %d", pid);
         name += ":";
-        output += tidTable.format(*this, uid, pid, name);
+        output += FormatTable(tidTable, uid, pid, name);
     }
 
     if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
         name = "Chattiest events log buffer TAGs";
         if (pid) name += android::base::StringPrintf(" for PID %d", pid);
         name += ":";
-        output += tagTable.format(*this, uid, pid, name, LOG_ID_EVENTS);
+        output += FormatTable(tagTable, uid, pid, name, LOG_ID_EVENTS);
     }
 
     if (enable && (logMask & (1 << LOG_ID_SECURITY))) {
         name = "Chattiest security log buffer TAGs";
         if (pid) name += android::base::StringPrintf(" for PID %d", pid);
         name += ":";
-        output +=
-            securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
+        output += FormatTable(securityTagTable, uid, pid, name, LOG_ID_SECURITY);
     }
 
     if (enable) {
         name = "Chattiest TAGs";
         if (pid) name += android::base::StringPrintf(" for PID %d", pid);
         name += ":";
-        output += tagNameTable.format(*this, uid, pid, name);
+        output += FormatTable(tagNameTable, uid, pid, name);
     }
 
     return output;
@@ -839,12 +945,14 @@
 }
 }
 
-uid_t LogStatistics::pidToUid(pid_t pid) {
+uid_t LogStatistics::PidToUid(pid_t pid) {
+    auto lock = std::lock_guard{lock_};
     return pidTable.add(pid)->second.getUid();
 }
 
 // caller must free character string
-const char* LogStatistics::pidToName(pid_t pid) const {
+const char* LogStatistics::PidToName(pid_t pid) const {
+    auto lock = std::lock_guard{lock_};
     // An inconvenient truth ... getName() can alter the object
     pidTable_t& writablePidTable = const_cast<pidTable_t&>(pidTable);
     const char* name = writablePidTable.add(pid)->second.getName();
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 0782de3..53c9bb6 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LOGD_LOG_STATISTICS_H__
-#define _LOGD_LOG_STATISTICS_H__
+#pragma once
 
 #include <ctype.h>
 #include <inttypes.h>
@@ -25,12 +24,15 @@
 #include <sys/types.h>
 
 #include <algorithm>  // std::max
+#include <array>
 #include <memory>
+#include <mutex>
 #include <string>
 #include <string_view>
 #include <unordered_map>
 
 #include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
 #include <android/log.h>
 #include <log/log_time.h>
 #include <private/android_filesystem_config.h>
@@ -79,16 +81,12 @@
     typedef
         typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator;
 
-    std::unique_ptr<const TEntry* []> sort(uid_t uid, pid_t pid,
-                                           size_t len) const {
-        if (!len) {
-            std::unique_ptr<const TEntry* []> sorted(nullptr);
-            return sorted;
-        }
-
-        const TEntry** retval = new const TEntry*[len];
-        memset(retval, 0, sizeof(*retval) * len);
-
+    // Returns a sorted array of up to len highest entries sorted by size.  If fewer than len
+    // entries are found, their positions are set to nullptr.
+    template <size_t len>
+    void MaxEntries(uid_t uid, pid_t pid, std::array<const TEntry*, len>* out) const {
+        auto& retval = *out;
+        retval.fill(nullptr);
         for (const_iterator it = map.begin(); it != map.end(); ++it) {
             const TEntry& entry = it->second;
 
@@ -113,8 +111,6 @@
                 retval[index] = &entry;
             }
         }
-        std::unique_ptr<const TEntry* []> sorted(retval);
-        return sorted;
     }
 
     inline iterator add(const TKey& key, const LogBufferElement* element) {
@@ -170,35 +166,6 @@
     inline const_iterator end() const {
         return map.end();
     }
-
-    std::string format(const LogStatistics& stat, uid_t uid, pid_t pid,
-                       const std::string& name = std::string(""),
-                       log_id_t id = LOG_ID_MAX) const {
-        static const size_t maximum_sorted_entries = 32;
-        std::string output;
-        std::unique_ptr<const TEntry* []> sorted =
-            sort(uid, pid, maximum_sorted_entries);
-        if (!sorted.get()) {
-            return output;
-        }
-        bool headerPrinted = false;
-        for (size_t index = 0; index < maximum_sorted_entries; ++index) {
-            const TEntry* entry = sorted[index];
-            if (!entry) {
-                break;
-            }
-            if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
-                break;
-            }
-            if (!headerPrinted) {
-                output += "\n\n";
-                output += entry->formatHeader(name, id);
-                headerPrinted = true;
-            }
-            output += entry->format(stat, id);
-        }
-        return output;
-    }
 };
 
 namespace EntryBaseConstants {
@@ -627,84 +594,51 @@
     std::string format(const LogStatistics& stat, log_id_t id) const;
 };
 
-template <typename TEntry>
-class LogFindWorst {
-    std::unique_ptr<const TEntry* []> sorted;
-
-   public:
-    explicit LogFindWorst(std::unique_ptr<const TEntry* []>&& sorted)
-        : sorted(std::move(sorted)) {
-    }
-
-    void findWorst(int& worst, size_t& worst_sizes, size_t& second_worst_sizes,
-                   size_t threshold) {
-        if (sorted.get() && sorted[0] && sorted[1]) {
-            worst_sizes = sorted[0]->getSizes();
-            if ((worst_sizes > threshold)
-                // Allow time horizon to extend roughly tenfold, assume
-                // average entry length is 100 characters.
-                && (worst_sizes > (10 * sorted[0]->getDropped()))) {
-                worst = sorted[0]->getKey();
-                second_worst_sizes = sorted[1]->getSizes();
-                if (second_worst_sizes < threshold) {
-                    second_worst_sizes = threshold;
-                }
-            }
-        }
-    }
-
-    void findWorst(int& worst, size_t worst_sizes, size_t& second_worst_sizes) {
-        if (sorted.get() && sorted[0] && sorted[1]) {
-            worst = sorted[0]->getKey();
-            second_worst_sizes =
-                worst_sizes - sorted[0]->getSizes() + sorted[1]->getSizes();
-        }
-    }
-};
-
 // Log Statistics
 class LogStatistics {
     friend UidEntry;
+    friend PidEntry;
+    friend TidEntry;
 
-    size_t mSizes[LOG_ID_MAX];
-    size_t mElements[LOG_ID_MAX];
-    size_t mDroppedElements[LOG_ID_MAX];
-    size_t mSizesTotal[LOG_ID_MAX];
-    size_t mElementsTotal[LOG_ID_MAX];
-    log_time mOldest[LOG_ID_MAX];
-    log_time mNewest[LOG_ID_MAX];
-    log_time mNewestDropped[LOG_ID_MAX];
-    static size_t SizesTotal;
+    size_t mSizes[LOG_ID_MAX] GUARDED_BY(lock_);
+    size_t mElements[LOG_ID_MAX] GUARDED_BY(lock_);
+    size_t mDroppedElements[LOG_ID_MAX] GUARDED_BY(lock_);
+    size_t mSizesTotal[LOG_ID_MAX] GUARDED_BY(lock_);
+    size_t mElementsTotal[LOG_ID_MAX] GUARDED_BY(lock_);
+    log_time mOldest[LOG_ID_MAX] GUARDED_BY(lock_);
+    log_time mNewest[LOG_ID_MAX] GUARDED_BY(lock_);
+    log_time mNewestDropped[LOG_ID_MAX] GUARDED_BY(lock_);
+    static std::atomic<size_t> SizesTotal;
     bool enable;
 
     // uid to size list
     typedef LogHashtable<uid_t, UidEntry> uidTable_t;
-    uidTable_t uidTable[LOG_ID_MAX];
+    uidTable_t uidTable[LOG_ID_MAX] GUARDED_BY(lock_);
 
     // pid of system to size list
     typedef LogHashtable<pid_t, PidEntry> pidSystemTable_t;
-    pidSystemTable_t pidSystemTable[LOG_ID_MAX];
+    pidSystemTable_t pidSystemTable[LOG_ID_MAX] GUARDED_BY(lock_);
 
     // pid to uid list
     typedef LogHashtable<pid_t, PidEntry> pidTable_t;
-    pidTable_t pidTable;
+    pidTable_t pidTable GUARDED_BY(lock_);
 
     // tid to uid list
     typedef LogHashtable<pid_t, TidEntry> tidTable_t;
-    tidTable_t tidTable;
+    tidTable_t tidTable GUARDED_BY(lock_);
 
     // tag list
     typedef LogHashtable<uint32_t, TagEntry> tagTable_t;
-    tagTable_t tagTable;
+    tagTable_t tagTable GUARDED_BY(lock_);
 
     // security tag list
-    tagTable_t securityTagTable;
+    tagTable_t securityTagTable GUARDED_BY(lock_);
 
     // global tag list
     typedef LogHashtable<TagNameKey, TagNameEntry> tagNameTable_t;
     tagNameTable_t tagNameTable;
 
-    size_t sizeOf() const {
+    size_t sizeOf() const REQUIRES(lock_) {
         size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() +
                       tagTable.sizeOf() + securityTagTable.sizeOf() +
                       tagNameTable.sizeOf() +
@@ -729,62 +663,59 @@
         return size;
     }
 
-   public:
-    LogStatistics();
+  public:
+    LogStatistics(bool enable_statistics);
 
-    void enableStatistics() {
-        enable = true;
-    }
-
-    void addTotal(LogBufferElement* entry);
-    void add(LogBufferElement* entry);
-    void subtract(LogBufferElement* entry);
+    void AddTotal(LogBufferElement* entry) EXCLUDES(lock_);
+    void Add(LogBufferElement* entry) EXCLUDES(lock_);
+    void Subtract(LogBufferElement* entry) EXCLUDES(lock_);
     // entry->setDropped(1) must follow this call
-    void drop(LogBufferElement* entry);
+    void Drop(LogBufferElement* entry) EXCLUDES(lock_);
     // Correct for coalescing two entries referencing dropped content
-    void erase(LogBufferElement* element) {
+    void Erase(LogBufferElement* element) EXCLUDES(lock_) {
+        auto lock = std::lock_guard{lock_};
         log_id_t log_id = element->getLogId();
         --mElements[log_id];
         --mDroppedElements[log_id];
     }
 
-    LogFindWorst<UidEntry> sort(uid_t uid, pid_t pid, size_t len, log_id id) {
-        return LogFindWorst<UidEntry>(uidTable[id].sort(uid, pid, len));
-    }
-    LogFindWorst<PidEntry> sortPids(uid_t uid, pid_t pid, size_t len,
-                                    log_id id) {
-        return LogFindWorst<PidEntry>(pidSystemTable[id].sort(uid, pid, len));
-    }
-    LogFindWorst<TagEntry> sortTags(uid_t uid, pid_t pid, size_t len, log_id) {
-        return LogFindWorst<TagEntry>(tagTable.sort(uid, pid, len));
-    }
+    void WorstTwoUids(log_id id, size_t threshold, int* worst, size_t* worst_sizes,
+                      size_t* second_worst_sizes) const EXCLUDES(lock_);
+    void WorstTwoTags(size_t threshold, int* worst, size_t* worst_sizes,
+                      size_t* second_worst_sizes) const EXCLUDES(lock_);
+    void WorstTwoSystemPids(log_id id, size_t worst_uid_sizes, int* worst,
+                            size_t* second_worst_sizes) const EXCLUDES(lock_);
 
-    // fast track current value by id only
-    size_t sizes(log_id_t id) const {
+    bool ShouldPrune(log_id id, unsigned long max_size, unsigned long* prune_rows) const
+            EXCLUDES(lock_);
+
+    // Snapshot of the sizes for a given log buffer.
+    size_t Sizes(log_id_t id) const EXCLUDES(lock_) {
+        auto lock = std::lock_guard{lock_};
         return mSizes[id];
     }
-    size_t elements(log_id_t id) const {
-        return mElements[id];
-    }
-    size_t realElements(log_id_t id) const {
-        return mElements[id] - mDroppedElements[id];
-    }
-    size_t sizesTotal(log_id_t id) const {
-        return mSizesTotal[id];
-    }
-    size_t elementsTotal(log_id_t id) const {
-        return mElementsTotal[id];
-    }
+    // TODO: Get rid of this entirely.
     static size_t sizesTotal() {
         return SizesTotal;
     }
 
-    std::string format(uid_t uid, pid_t pid, unsigned int logMask) const;
+    std::string Format(uid_t uid, pid_t pid, unsigned int logMask) const EXCLUDES(lock_);
 
-    // helper (must be locked directly or implicitly by mLogElementsLock)
-    const char* pidToName(pid_t pid) const;
-    uid_t pidToUid(pid_t pid);
-    const char* uidToName(uid_t uid) const;
+    const char* PidToName(pid_t pid) const EXCLUDES(lock_);
+    uid_t PidToUid(pid_t pid) EXCLUDES(lock_);
+    const char* UidToName(uid_t uid) const EXCLUDES(lock_);
+
+  private:
+    template <typename TKey, typename TEntry>
+    void WorstTwoWithThreshold(const LogHashtable<TKey, TEntry>& table, size_t threshold,
+                               int* worst, size_t* worst_sizes, size_t* second_worst_sizes) const;
+    template <typename TKey, typename TEntry>
+    std::string FormatTable(const LogHashtable<TKey, TEntry>& table, uid_t uid, pid_t pid,
+                            const std::string& name = std::string(""),
+                            log_id_t id = LOG_ID_MAX) const REQUIRES(lock_);
+    void FormatTmp(const char* nameTmp, uid_t uid, std::string& name, std::string& size,
+                   size_t nameLen) const REQUIRES(lock_);
+    const char* UidToNameLocked(uid_t uid) const REQUIRES(lock_);
+
+    mutable std::mutex lock_;
 };
-
-#endif  // _LOGD_LOG_STATISTICS_H__
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 3e52b38..8250808 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -37,6 +37,7 @@
 #include <log/log_read.h>
 #include <private/android_filesystem_config.h>
 
+#include "LogStatistics.h"
 #include "LogTags.h"
 #include "LogUtils.h"
 
@@ -99,8 +100,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),
@@ -189,7 +189,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);
@@ -495,7 +494,7 @@
 
     // Every 16K (half the smallest configurable pmsg buffer size) record
     static const size_t rate_to_pmsg = 16 * 1024;
-    if (lastTotal && ((android::sizesTotal() - lastTotal) < rate_to_pmsg)) {
+    if (lastTotal && (LogStatistics::sizesTotal() - lastTotal) < rate_to_pmsg) {
         return;
     }
 
@@ -548,7 +547,7 @@
      */
 
     struct timespec ts;
-    clock_gettime(android_log_clockid(), &ts);
+    clock_gettime(CLOCK_REALTIME, &ts);
 
     android_log_header_t header = {
         .id = LOG_ID_EVENTS,
@@ -665,7 +664,7 @@
         }
     }
 
-    lastTotal = android::sizesTotal();
+    lastTotal = LogStatistics::sizesTotal();
     if (!lastTotal) ++lastTotal;
 
     // record totals for next watermark.
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
deleted file mode 100644
index ad150bd..0000000
--- a/logd/LogTimes.cpp
+++ /dev/null
@@ -1,253 +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 "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, bool privileged,
-                           bool can_read_security_logs)
-    : leadingDropped(false),
-      mReader(reader),
-      mLogMask(logMask),
-      mPid(pid),
-      mCount(0),
-      mTail(tail),
-      mIndex(0),
-      mClient(client),
-      mStartTime(start_time),
-      mStart(start),
-      mNonBlock(nonBlock),
-      privileged_(privileged),
-      can_read_security_logs_(can_read_security_logs) {
-    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();
-
-    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, me->privileged_, me->can_read_security_logs_,
-                           FilterFirstPass, me);
-            me->leadingDropped = true;
-        }
-        start = logbuf.flushTo(client, start, me->mLastTid, me->privileged_,
-                               me->can_read_security_logs_, 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 56c930a..0000000
--- a/logd/LogTimes.h
+++ /dev/null
@@ -1,109 +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, bool privileged, bool can_read_security_logs);
-
-    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);
-
-  private:
-    bool privileged_;
-    bool can_read_security_logs_;
-};
-
-typedef std::list<std::unique_ptr<LogTimeEntry>> LastLogTimes;
-
-#endif  // _LOGD_LOG_TIMES_H__
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index f9cd42d..3fe1bbe 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -32,8 +32,6 @@
 char* uidToName(uid_t uid);
 void prdebug(const char* fmt, ...) __printflike(1, 2);
 
-// Furnished in LogStatistics.cpp.
-size_t sizesTotal();
 // Caller must own and free returned value
 char* pidToName(pid_t pid);
 char* tidToName(pid_t tid);
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/Android.bp b/logd/fuzz/Android.bp
index 299242d..f65fbdf 100644
--- a/logd/fuzz/Android.bp
+++ b/logd/fuzz/Android.bp
@@ -25,6 +25,7 @@
         "liblog",
         "liblogd",
         "libcutils",
+        "libsysutils",
     ],
     cflags: ["-Werror"],
 }
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index 14c5163..8f90f50 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -15,8 +15,10 @@
  */
 #include <string>
 
-#include "../LogBuffer.h"
-#include "../LogTimes.h"
+#include "../ChattyLogBuffer.h"
+#include "../LogReaderList.h"
+#include "../LogReaderThread.h"
+#include "../LogStatistics.h"
 
 // We don't want to waste a lot of entropy on messages
 #define MAX_MSG_LENGTH 5
@@ -36,7 +38,8 @@
     unsigned int log_mask;
 };
 
-int write_log_messages(const uint8_t** pdata, size_t* data_left, LogBuffer* log_buffer) {
+int write_log_messages(const uint8_t** pdata, size_t* data_left, LogBuffer* log_buffer,
+                       LogStatistics* stats) {
     const uint8_t* data = *pdata;
     const LogInput* logInput = reinterpret_cast<const LogInput*>(data);
     data += sizeof(LogInput);
@@ -69,9 +72,9 @@
 
     // Other elements not in enum.
     log_id_t log_id = static_cast<log_id_t>(unsigned(logInput->log_id) % (LOG_ID_MAX + 1));
-    log_buffer->log(log_id, logInput->realtime, logInput->uid, logInput->pid, logInput->tid, msg,
+    log_buffer->Log(log_id, logInput->realtime, logInput->uid, logInput->pid, logInput->tid, msg,
                     sizeof(uint32_t) + msg_length + 1);
-    log_buffer->formatStatistics(logInput->uid, logInput->pid, logInput->log_mask);
+    stats->Format(logInput->uid, logInput->pid, logInput->log_mask);
     *pdata = data;
     return 1;
 }
@@ -93,25 +96,25 @@
         return 0;
     }
 
-    LastLogTimes times;
+    LogReaderList reader_list;
     LogTags tags;
     PruneList prune_list;
-    LogBuffer log_buffer(&times, &tags, &prune_list);
+    LogStatistics stats(true);
+    LogBuffer* log_buffer = new ChattyLogBuffer(&reader_list, &tags, &prune_list, &stats);
     size_t data_left = size;
     const uint8_t** pdata = &data;
 
-    log_buffer.enableStatistics();
     prune_list.init(nullptr);
     // We want to get pruning code to get called.
-    log_id_for_each(i) { log_buffer.setSize(i, 10000); }
+    log_id_for_each(i) { log_buffer->SetSize(i, 10000); }
 
     while (data_left >= sizeof(LogInput) + 2 * sizeof(uint8_t)) {
-        if (!write_log_messages(pdata, &data_left, &log_buffer)) {
+        if (!write_log_messages(pdata, &data_left, log_buffer, &stats)) {
             return 0;
         }
     }
 
-    log_id_for_each(i) { log_buffer.clear(i); }
+    log_id_for_each(i) { log_buffer->Clear(i, 0); }
     return 0;
 }
 }  // namespace android
diff --git a/logd/main.cpp b/logd/main.cpp
index cc45eb3..6e1144b 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -47,11 +47,13 @@
 #include <processgroup/sched_policy.h>
 #include <utils/threads.h>
 
+#include "ChattyLogBuffer.h"
 #include "CommandListener.h"
 #include "LogAudit.h"
 #include "LogBuffer.h"
 #include "LogKlog.h"
 #include "LogListener.h"
+#include "LogStatistics.h"
 #include "LogTags.h"
 #include "LogUtils.h"
 
@@ -109,21 +111,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) {
@@ -201,10 +188,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));
@@ -289,31 +272,28 @@
 
     // A cache of event log tags
     LogTags log_tags;
+
     // Pruning configuration.
     PruneList prune_list;
 
+    // Partial (required for chatty) or full logging statistics.
+    bool enable_full_log_statistics = __android_logger_property_get_bool(
+            "logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
+                                       BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
+    LogStatistics log_statistics(enable_full_log_statistics);
+
     // Serves the purpose of managing the last logs times read on a
     // socket connection, and as a reader lock on a range of log
     // entries.
-
-    LastLogTimes* times = new LastLogTimes();
+    LogReaderList reader_list;
 
     // LogBuffer is the object which is responsible for holding all
     // log entries.
-
-    LogBuffer* logBuf = new LogBuffer(times, &log_tags, &prune_list);
-
-    if (__android_logger_property_get_bool(
-            "logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
-                                   BOOL_DEFAULT_FLAG_ENG |
-                                   BOOL_DEFAULT_FLAG_SVELTE)) {
-        logBuf->enableStatistics();
-    }
+    LogBuffer* logBuf = new ChattyLogBuffer(&reader_list, &log_tags, &prune_list, &log_statistics);
 
     // LogReader listens on /dev/socket/logdr. When a client
     // connects, log entries in the LogBuffer are written to the client.
-
-    LogReader* reader = new LogReader(logBuf);
+    LogReader* reader = new LogReader(logBuf, &reader_list);
     if (reader->startListener()) {
         return EXIT_FAILURE;
     }
@@ -321,17 +301,14 @@
     // LogListener listens on /dev/socket/logdw for client
     // initiated log messages. New log entries are added to LogBuffer
     // and LogReader is notified to send updates to connected clients.
-
-    LogListener* swl = new LogListener(logBuf, reader);
-    // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
-    if (swl->startListener(600)) {
+    LogListener* swl = new LogListener(logBuf);
+    if (!swl->StartListener()) {
         return EXIT_FAILURE;
     }
 
     // Command listener listens on /dev/socket/logd for incoming logd
     // administrative commands.
-
-    CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list);
+    CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list, &log_statistics);
     if (cl->startListener()) {
         return EXIT_FAILURE;
     }
@@ -339,25 +316,22 @@
     // LogAudit listens on NETLINK_AUDIT socket for selinux
     // initiated log messages. New log entries are added to LogBuffer
     // and LogReader is notified to send updates to connected clients.
-
     LogAudit* al = nullptr;
     if (auditd) {
-        al = new LogAudit(logBuf, reader,
-                          __android_logger_property_get_bool(
-                              "ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
-                              ? fdDmesg
-                              : -1);
+        int dmesg_fd = __android_logger_property_get_bool("ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
+                               ? fdDmesg
+                               : -1;
+        al = new LogAudit(logBuf, dmesg_fd, &log_statistics);
     }
 
     LogKlog* kl = nullptr;
     if (klogd) {
-        kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != nullptr);
+        kl = new LogKlog(logBuf, fdDmesg, fdPmesg, al != nullptr, &log_statistics);
     }
 
     readDmesg(al, kl);
 
     // failure is an option ... messages are in dmesg (required by standard)
-
     if (kl && kl->startListener()) {
         delete kl;
     }
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 1dd5c86..55737e9 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -606,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) {
@@ -682,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;
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a380ebb..00a58bf 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -547,8 +547,8 @@
     enter_default_mount_ns
 
     # /data/apex is now available. Start apexd to scan and activate APEXes.
-    mkdir /data/apex 0750 root system encryption=None
-    mkdir /data/apex/active 0750 root system
+    mkdir /data/apex 0755 root system encryption=None
+    mkdir /data/apex/active 0755 root system
     mkdir /data/apex/backup 0700 root system
     mkdir /data/apex/hashtree 0700 root system
     mkdir /data/apex/sessions 0700 root system