logging: update from upstream

Followed instructions from go/nnapi-dep-instructions.

No manual changes / interventions have been made.

$ git merge cros/upstream/master --no-ff
$ repo upload --cbr . --no-verify

BUG=b:177048246
TEST=cq passes

Exempt-From-Owner-Approval: This is a forked repo
Change-Id: Idbbd2d712b528176662694cebf869c0382587822
diff --git a/OWNERS b/OWNERS
index 096c4c5..7529cb9 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,2 +1 @@
-enh@google.com
-tomcherry@google.com
+include platform/system/core:/janitors/OWNERS
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..5e72035
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsLiblogTestCases"
+    },
+    {
+      "name": "CtsLogdTestCases"
+    },
+    {
+      "name": "liblog-host-test",
+      "host": true
+    },
+    {
+      "name": "logd-unit-tests",
+      "host": true
+    }
+  ]
+}
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 8f15541..32bdde0 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -36,7 +36,9 @@
     name: "liblog_headers",
     host_supported: true,
     vendor_available: true,
+    product_available: true,
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     apex_available: [
         "//apex_available:platform",
@@ -58,6 +60,9 @@
         vendor: {
             override_export_include_dirs: ["include_vndk"],
         },
+        product: {
+            override_export_include_dirs: ["include_vndk"],
+        },
     },
 }
 
@@ -67,8 +72,10 @@
     name: "liblog",
     host_supported: true,
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
+    llndk_stubs: "liblog.llndk",
     srcs: liblog_sources,
 
     target: {
@@ -131,6 +138,10 @@
         "com.android.runtime",
         // DO NOT add more apex names here
     ],
+    pgo: {
+        sampling: true,
+        profile_file: "logging/liblog.profdata",
+    },
 }
 
 ndk_headers {
@@ -149,7 +160,7 @@
 }
 
 llndk_library {
-    name: "liblog",
+    name: "liblog.llndk",
     native_bridge_supported: true,
     symbol_file: "liblog.map.txt",
     export_include_dirs: ["include_vndk"],
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index f5d19ca..3d5cee6 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -38,50 +38,82 @@
 #include "logger.h"
 #include "uio.h"
 
-static atomic_int logd_socket;
-
-// Note that it is safe to call connect() multiple times on DGRAM Unix domain sockets, so this
-// function is used to reconnect to logd without requiring a new socket.
-static void LogdConnect() {
-  sockaddr_un un = {};
-  un.sun_family = AF_UNIX;
-  strcpy(un.sun_path, "/dev/socket/logdw");
-  TEMP_FAILURE_RETRY(connect(logd_socket, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un)));
-}
-
-// logd_socket should only be opened once.  If we see that logd_socket is uninitialized, we create a
-// new socket and attempt to exchange it into the atomic logd_socket.  If the compare/exchange was
-// successful, then that will be the socket used for the duration of the program, otherwise a
-// different thread has already opened and written the socket to the atomic, so close the new socket
-// and return.
-static void GetSocket() {
-  if (logd_socket != 0) {
-    return;
+class LogdSocket {
+ public:
+  static LogdSocket& BlockingSocket() {
+    static LogdSocket logd_socket(true);
+    return logd_socket;
+  }
+  static LogdSocket& NonBlockingSocket() {
+    static LogdSocket logd_socket(false);
+    return logd_socket;
   }
 
-  int new_socket = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0));
-  if (new_socket <= 0) {
-    return;
+  void Reconnect() { LogdConnect(sock_); }
+
+  // Zygote uses this to clean up open FD's after fork() and before specialization.  It is single
+  // threaded at this point and therefore this function is explicitly not thread safe.  It sets
+  // sock_ to kUninitialized, so future logs will be safely initialized whenever they happen.
+  void Close() {
+    if (sock_ != kUninitialized) {
+      close(sock_);
+    }
+    sock_ = kUninitialized;
   }
 
-  int uninitialized_value = 0;
-  if (!logd_socket.compare_exchange_strong(uninitialized_value, new_socket)) {
-    close(new_socket);
-    return;
+  int sock() {
+    GetSocket();
+    return sock_;
   }
 
-  LogdConnect();
-}
+ private:
+  LogdSocket(bool blocking) : blocking_(blocking) {}
 
-// This is the one exception to the above.  Zygote uses this to clean up open FD's after fork() and
-// before specialization.  It is single threaded at this point and therefore this function is
-// explicitly not thread safe.  It sets logd_socket to 0, so future logs will be safely initialized
-// whenever they happen.
+  // Note that it is safe to call connect() multiple times on DGRAM Unix domain sockets, so this
+  // function is used to reconnect to logd without requiring a new socket.
+  static void LogdConnect(int sock) {
+    sockaddr_un un = {};
+    un.sun_family = AF_UNIX;
+    strcpy(un.sun_path, "/dev/socket/logdw");
+    TEMP_FAILURE_RETRY(connect(sock, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un)));
+  }
+
+  // sock_ should only be opened once.  If we see that sock_ is uninitialized, we
+  // create a new socket and attempt to exchange it into the atomic sock_.  If the
+  // compare/exchange was successful, then that will be the socket used for the duration of the
+  // program, otherwise a different thread has already opened and written the socket to the atomic,
+  // so close the new socket and return.
+  void GetSocket() {
+    if (sock_ != kUninitialized) {
+      return;
+    }
+
+    int flags = SOCK_DGRAM | SOCK_CLOEXEC;
+    if (!blocking_) {
+      flags |= SOCK_NONBLOCK;
+    }
+    int new_socket = TEMP_FAILURE_RETRY(socket(PF_UNIX, flags, 0));
+    if (new_socket < 0) {
+      return;
+    }
+
+    LogdConnect(new_socket);
+
+    int uninitialized_value = kUninitialized;
+    if (!sock_.compare_exchange_strong(uninitialized_value, new_socket)) {
+      close(new_socket);
+      return;
+    }
+  }
+
+  static const int kUninitialized = -1;
+  atomic_int sock_ = kUninitialized;
+  bool blocking_;
+};
+
 void LogdClose() {
-  if (logd_socket > 0) {
-    close(logd_socket);
-  }
-  logd_socket = 0;
+  LogdSocket::BlockingSocket().Close();
+  LogdSocket::NonBlockingSocket().Close();
 }
 
 int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
@@ -90,10 +122,12 @@
   struct iovec newVec[nr + headerLength];
   android_log_header_t header;
   size_t i, payloadSize;
+  static atomic_int dropped;
 
-  GetSocket();
+  LogdSocket& logd_socket =
+      logId == LOG_ID_SECURITY ? LogdSocket::BlockingSocket() : LogdSocket::NonBlockingSocket();
 
-  if (logd_socket <= 0) {
+  if (logd_socket.sock() < 0) {
     return -EBADF;
   }
 
@@ -107,7 +141,6 @@
     return 0;
   }
 
-  header.id = logId;
   header.tid = gettid();
   header.realtime.tv_sec = ts->tv_sec;
   header.realtime.tv_nsec = ts->tv_nsec;
@@ -115,6 +148,27 @@
   newVec[0].iov_base = (unsigned char*)&header;
   newVec[0].iov_len = sizeof(header);
 
+  int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+  if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog", strlen("liblog"),
+                                                ANDROID_LOG_VERBOSE)) {
+    android_log_event_int_t buffer;
+
+    header.id = LOG_ID_EVENTS;
+    buffer.header.tag = LIBLOG_LOG_TAG;
+    buffer.payload.type = EVENT_TYPE_INT;
+    buffer.payload.data = snapshot;
+
+    newVec[headerLength].iov_base = &buffer;
+    newVec[headerLength].iov_len = sizeof(buffer);
+
+    ret = TEMP_FAILURE_RETRY(writev(logd_socket.sock(), newVec, 2));
+    if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+      atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
+    }
+  }
+
+  header.id = logId;
+
   for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
     newVec[i].iov_base = vec[i - headerLength].iov_base;
     payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
@@ -128,16 +182,24 @@
     }
   }
 
-  ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
-  if (ret < 0) {
-    LogdConnect();
+  // EAGAIN occurs if logd is overloaded, other errors indicate that something went wrong with
+  // the connection, so we reset it and try again.
+  ret = TEMP_FAILURE_RETRY(writev(logd_socket.sock(), newVec, i));
+  if (ret < 0 && errno != EAGAIN) {
+    logd_socket.Reconnect();
 
-    ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
+    ret = TEMP_FAILURE_RETRY(writev(logd_socket.sock(), newVec, i));
   }
 
   if (ret < 0) {
     ret = -errno;
   }
 
+  if (ret > (ssize_t)sizeof(header)) {
+    ret -= sizeof(header);
+  } else if (ret < 0) {
+    atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+  }
+
   return ret;
 }
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 8e676bd..037a72c 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -31,24 +31,32 @@
 
 static atomic_int pmsg_fd;
 
-// pmsg_fd should only beopened once.  If we see that pmsg_fd is uninitialized, we open "/dev/pmsg0"
-// then attempt to compare/exchange it into pmsg_fd.  If the compare/exchange was successful, then
-// that will be the fd used for the duration of the program, otherwise a different thread has
-// already opened and written the fd to the atomic, so close the new fd and return.
 static void GetPmsgFd() {
+  // Note if open() fails and returns -1, that value is stored into pmsg_fd as an indication that
+  // pmsg is not available and open() should not be retried.
   if (pmsg_fd != 0) {
     return;
   }
 
   int new_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
-  if (new_fd <= 0) {
-    return;
+
+  // Unlikely that new_fd is 0, but that is synonymous with our uninitialized value, and we'd prefer
+  // STDIN_FILENO != stdin, so we call open() to get a new fd value in this case.
+  if (new_fd == 0) {
+    new_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+    close(0);
   }
 
+  // pmsg_fd should only be opened once.  If we see that pmsg_fd is uninitialized, we open
+  // "/dev/pmsg0" then attempt to compare/exchange it into pmsg_fd.  If the compare/exchange was
+  // successful, then that will be the fd used for the duration of the program, otherwise a
+  // different thread has already opened and written the fd to the atomic, so close the new fd and
+  // return.
   int uninitialized_value = 0;
   if (!pmsg_fd.compare_exchange_strong(uninitialized_value, new_fd)) {
-    close(new_fd);
-    return;
+    if (new_fd != -1) {
+      close(new_fd);
+    }
   }
 }
 
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 171aafd..7524750 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -99,6 +99,7 @@
         "cts",
         "device-tests",
     ],
+    test_config: "device_test_config.xml",
 }
 
 cc_test_host {
@@ -111,4 +112,7 @@
         "liblog_global_state.cpp",
     ],
     isolated: true,
+    test_suites: [
+        "general-tests",
+    ],
 }
diff --git a/liblog/tests/AndroidTest.xml b/liblog/tests/device_test_config.xml
similarity index 100%
rename from liblog/tests/AndroidTest.xml
rename to liblog/tests/device_test_config.xml
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index d2f12d6..5f1750b 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -594,8 +594,8 @@
     // In system/core/logcat/event.logtags:
     // # These are used for testing, do not modify without updating
     // # tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
-    // # system/core/liblog/tests/liblog_benchmark.cpp
-    // # system/core/liblog/tests/liblog_test.cpp
+    // # system/logging/liblog/tests/liblog_benchmark.cpp
+    // # system/logging/liblog/tests/liblog_test.cpp
     // 42    answer (to life the universe etc|3)
     __android_log_btwrite(42, EVENT_TYPE_LONG, &i, sizeof(i));
     state.PauseTiming();
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 93c3d6d..210cac7 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -37,8 +37,8 @@
 
 # These are used for testing, do not modify without updating
 # tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
-# system/core/liblog/tests/liblog_benchmark.cpp
-# system/core/liblog/tests/liblog_test.cpp
+# system/logging/liblog/tests/liblog_benchmark.cpp
+# system/logging/liblog/tests/liblog_test.cpp
 42    answer (to life the universe etc|3)
 314   pi
 2718  e
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 6b7e016..67ee676 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -101,6 +101,7 @@
     bool print_it_anyways_ = false;
 
     // For PrintDividers()
+    bool print_dividers_ = false;
     log_id_t last_printed_id_ = LOG_ID_MAX;
     bool printed_start_[LOG_ID_MAX] = {};
 
@@ -215,6 +216,8 @@
 
         print_count_ += match;
         if (match || print_it_anyways_) {
+            PrintDividers(buf->id(), print_dividers_);
+
             bytesWritten = android_log_printLogLine(logformat_.get(), output_fd_.get(), &entry);
 
             if (bytesWritten < 0) {
@@ -231,7 +234,7 @@
 }
 
 void Logcat::PrintDividers(log_id_t log_id, bool print_dividers) {
-    if (log_id == last_printed_id_ || print_binary_) {
+    if (log_id == last_printed_id_) {
         return;
     }
     if (!printed_start_[log_id] || print_dividers) {
@@ -530,7 +533,6 @@
     bool getLogSize = false;
     bool getPruneList = false;
     bool printStatistics = false;
-    bool printDividers = false;
     unsigned long setLogSize = 0;
     const char* setPruneList = nullptr;
     const char* setId = nullptr;
@@ -696,7 +698,7 @@
                 break;
 
             case 'D':
-                printDividers = true;
+                print_dividers_ = true;
                 break;
 
             case 'e':
@@ -1193,8 +1195,6 @@
             continue;
         }
 
-        PrintDividers(log_msg.id(), printDividers);
-
         if (print_binary_) {
             if (!WriteFully(output_fd_, &log_msg, log_msg.len())) {
                 error(EXIT_FAILURE, errno, "Failed to write to output fd");
diff --git a/logd/Android.bp b/logd/Android.bp
index 829c95f..63bd505 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -91,6 +91,7 @@
         "LogAudit.cpp",
         "LogKlog.cpp",
         "libaudit.cpp",
+        "PkgIds.cpp",
     ],
 
     static_libs: [
@@ -172,6 +173,9 @@
     name: "logd-unit-tests",
     host_supported: true,
     defaults: ["logd-unit-test-defaults"],
+    test_suites: [
+        "general-tests",
+    ],
 }
 
 cc_test {
@@ -189,6 +193,7 @@
         "cts",
         "device-tests",
     ],
+    test_config: "device_test_config.xml",
 }
 
 cc_binary {
diff --git a/logd/ChattyLogBufferTest.cpp b/logd/ChattyLogBufferTest.cpp
index e273efe..dd9375d 100644
--- a/logd/ChattyLogBufferTest.cpp
+++ b/logd/ChattyLogBufferTest.cpp
@@ -59,16 +59,6 @@
     FixupMessages(&log_messages);
     LogMessages(log_messages);
 
-    std::vector<LogMessage> read_log_messages;
-    {
-        auto lock = std::lock_guard{logd_lock};
-        std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
-
-        std::unique_ptr<FlushToState> flush_to_state =
-                log_buffer_->CreateFlushToState(1, kLogMaskAll);
-        EXPECT_TRUE(log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr));
-    }
-
     std::vector<LogMessage> expected_log_messages = {
             make_message(0, "test_tag", "duplicate"),
             make_message(1, "test_tag", "duplicate"),
@@ -92,7 +82,8 @@
             make_message(300, "test_tag", "duplicate"),
     };
     FixupMessages(&expected_log_messages);
-    CompareLogMessages(expected_log_messages, read_log_messages);
+    auto flush_result = FlushMessages();
+    CompareLogMessages(expected_log_messages, flush_result.messages);
 };
 
 TEST_P(ChattyLogBufferTest, deduplication_overflow) {
@@ -121,15 +112,6 @@
     FixupMessages(&log_messages);
     LogMessages(log_messages);
 
-    std::vector<LogMessage> read_log_messages;
-    {
-        auto lock = std::lock_guard{logd_lock};
-        std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
-        std::unique_ptr<FlushToState> flush_to_state =
-                log_buffer_->CreateFlushToState(1, kLogMaskAll);
-        EXPECT_TRUE(log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr));
-    }
-
     std::vector<LogMessage> expected_log_messages = {
             make_message(0, "test_tag", "normal"),
             make_message(1, "test_tag", "duplicate"),
@@ -141,7 +123,8 @@
             make_message(expired_per_chatty_message + 4, "test_tag", "normal"),
     };
     FixupMessages(&expected_log_messages);
-    CompareLogMessages(expected_log_messages, read_log_messages);
+    auto flush_result = FlushMessages();
+    CompareLogMessages(expected_log_messages, flush_result.messages);
 }
 
 TEST_P(ChattyLogBufferTest, deduplication_liblog) {
@@ -181,15 +164,6 @@
     FixupMessages(&log_messages);
     LogMessages(log_messages);
 
-    std::vector<LogMessage> read_log_messages;
-    {
-        auto lock = std::lock_guard{logd_lock};
-        std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
-        std::unique_ptr<FlushToState> flush_to_state =
-                log_buffer_->CreateFlushToState(1, kLogMaskAll);
-        EXPECT_TRUE(log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr));
-    }
-
     std::vector<LogMessage> expected_log_messages = {
             make_message(0, 1234, 1),
             make_message(1, LIBLOG_LOG_TAG, 3),
@@ -212,7 +186,8 @@
             make_message(20, 1234, 227),
     };
     FixupMessages(&expected_log_messages);
-    CompareLogMessages(expected_log_messages, read_log_messages);
+    auto flush_result = FlushMessages();
+    CompareLogMessages(expected_log_messages, flush_result.messages);
 };
 
 TEST_P(ChattyLogBufferTest, no_leading_chatty_simple) {
@@ -267,21 +242,7 @@
     FixupMessages(&expected_log_messages);
     // clang-format on
 
-    std::vector<LogMessage> read_log_messages;
-    bool released = false;
-    {
-        auto lock = std::lock_guard{logd_lock};
-        std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
-        std::unique_ptr<LogReaderThread> log_reader(
-                new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
-                                    0, ~0, 1, {}, 2, {}));
-        reader_list_.reader_threads().emplace_back(std::move(log_reader));
-    }
-
-    while (!released) {
-        usleep(5000);
-    }
-
+    auto read_log_messages = ReadLogMessagesNonBlockingThread({.pid = 1, .sequence = 2});
     CompareLogMessages(expected_log_messages, read_log_messages);
 }
 
@@ -327,21 +288,7 @@
     FixupMessages(&expected_log_messages);
     // clang-format on
 
-    std::vector<LogMessage> read_log_messages;
-    bool released = false;
-    {
-        auto lock = std::lock_guard{logd_lock};
-        std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
-        std::unique_ptr<LogReaderThread> log_reader(
-                new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
-                                    3, ~0, 0, {}, 1, {}));
-        reader_list_.reader_threads().emplace_back(std::move(log_reader));
-    }
-
-    while (!released) {
-        usleep(5000);
-    }
-
+    auto read_log_messages = ReadLogMessagesNonBlockingThread({.tail = 3});
     CompareLogMessages(expected_log_messages, read_log_messages);
 }
 
diff --git a/logd/LogBufferTest.cpp b/logd/LogBufferTest.cpp
index cb9f428..e881e76 100644
--- a/logd/LogBufferTest.cpp
+++ b/logd/LogBufferTest.cpp
@@ -189,16 +189,9 @@
     FixupMessages(&log_messages);
     LogMessages(log_messages);
 
-    std::vector<LogMessage> read_log_messages;
-    {
-        auto lock = std::lock_guard{logd_lock};
-        std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
-        std::unique_ptr<FlushToState> flush_to_state =
-                log_buffer_->CreateFlushToState(1, kLogMaskAll);
-        EXPECT_TRUE(log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr));
-        EXPECT_EQ(2ULL, flush_to_state->start());
-    }
-    CompareLogMessages(log_messages, read_log_messages);
+    auto flush_result = FlushMessages();
+    EXPECT_EQ(2ULL, flush_result.next_sequence);
+    CompareLogMessages(log_messages, flush_result.messages);
 }
 
 TEST_P(LogBufferTest, smoke_with_reader_thread) {
@@ -227,25 +220,7 @@
     FixupMessages(&log_messages);
     LogMessages(log_messages);
 
-    std::vector<LogMessage> read_log_messages;
-    bool released = false;
-
-    {
-        auto lock = std::lock_guard{logd_lock};
-        std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
-        std::unique_ptr<LogReaderThread> log_reader(
-                new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
-                                    0, kLogMaskAll, 0, {}, 1, {}));
-        reader_list_.reader_threads().emplace_back(std::move(log_reader));
-    }
-
-    while (!released) {
-        usleep(5000);
-    }
-    {
-        auto lock = std::lock_guard{logd_lock};
-        EXPECT_EQ(0U, reader_list_.reader_threads().size());
-    }
+    auto read_log_messages = ReadLogMessagesNonBlockingThread({});
     CompareLogMessages(log_messages, read_log_messages);
 }
 
@@ -293,33 +268,20 @@
     return {entry, message};
 }
 
-TEST_P(LogBufferTest, random_messages) {
+std::vector<LogMessage> GenerateRandomLogMessages(size_t count) {
     srand(1);
     std::vector<LogMessage> log_messages;
-    for (size_t i = 0; i < 1000; ++i) {
+    for (size_t i = 0; i < count; ++i) {
         log_messages.emplace_back(GenerateRandomLogMessage(i));
     }
+    return log_messages;
+}
+
+TEST_P(LogBufferTest, random_messages) {
+    auto log_messages = GenerateRandomLogMessages(1000);
     LogMessages(log_messages);
 
-    std::vector<LogMessage> read_log_messages;
-    bool released = false;
-
-    {
-        auto lock = std::lock_guard{logd_lock};
-        std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
-        std::unique_ptr<LogReaderThread> log_reader(
-                new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
-                                    0, kLogMaskAll, 0, {}, 1, {}));
-        reader_list_.reader_threads().emplace_back(std::move(log_reader));
-    }
-
-    while (!released) {
-        usleep(5000);
-    }
-    {
-        auto lock = std::lock_guard{logd_lock};
-        EXPECT_EQ(0U, reader_list_.reader_threads().size());
-    }
+    auto read_log_messages = ReadLogMessagesNonBlockingThread({});
     CompareLogMessages(log_messages, read_log_messages);
 }
 
@@ -335,26 +297,8 @@
     FixupMessages(&log_messages);
     LogMessages(log_messages);
 
-    std::vector<LogMessage> read_log_messages;
-    bool released = false;
-
-    {
-        auto lock = std::lock_guard{logd_lock};
-        std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
-        std::unique_ptr<LogReaderThread> log_reader(
-                new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
-                                    0, kLogMaskAll, 0, {}, 3, {}));
-        reader_list_.reader_threads().emplace_back(std::move(log_reader));
-    }
-
-    while (!released) {
-        usleep(5000);
-    }
-    {
-        auto lock = std::lock_guard{logd_lock};
-        EXPECT_EQ(0U, reader_list_.reader_threads().size());
-    }
     std::vector<LogMessage> expected_log_messages = {log_messages.back()};
+    auto read_log_messages = ReadLogMessagesNonBlockingThread({.sequence = 3});
     CompareLogMessages(expected_log_messages, read_log_messages);
 }
 
@@ -371,18 +315,8 @@
     FixupMessages(&log_messages);
     LogMessages(log_messages);
 
-    std::vector<LogMessage> read_log_messages;
-    bool released = false;
-
     // Connect a blocking reader.
-    {
-        auto lock = std::lock_guard{logd_lock};
-        std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
-        std::unique_ptr<LogReaderThread> log_reader(
-                new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), false,
-                                    0, kLogMaskAll, 0, {}, 1, {}));
-        reader_list_.reader_threads().emplace_back(std::move(log_reader));
-    }
+    auto blocking_reader = TestReaderThread({.non_block = false}, *this);
 
     // Wait up to 250ms for the reader to read the first 3 logs.
     constexpr int kMaxRetryCount = 50;
@@ -421,37 +355,130 @@
     }
     ASSERT_LT(count, kMaxRetryCount);
 
-    // Release the reader, wait for it to get the signal then check that it has been deleted.
-    {
-        auto lock = std::lock_guard{logd_lock};
-        reader_list_.reader_threads().back()->Release();
-    }
-    while (!released) {
-        usleep(5000);
-    }
-    {
-        auto lock = std::lock_guard{logd_lock};
-        EXPECT_EQ(0U, reader_list_.reader_threads().size());
-    }
+    ReleaseAndJoinReaders();
 
     // Check that we have read all 6 messages.
     std::vector<LogMessage> expected_log_messages = log_messages;
     expected_log_messages.insert(expected_log_messages.end(), after_clear_messages.begin(),
                                  after_clear_messages.end());
-    CompareLogMessages(expected_log_messages, read_log_messages);
+    CompareLogMessages(expected_log_messages, blocking_reader.read_log_messages());
 
-    // Finally, call FlushTo and ensure that only the 3 logs after the clear remain in the buffer.
-    std::vector<LogMessage> read_log_messages_after_clear;
-    {
+    // Finally, Flush messages and ensure that only the 3 logs after the clear remain in the buffer.
+    auto flush_after_clear_result = FlushMessages();
+    EXPECT_EQ(7ULL, flush_after_clear_result.next_sequence);
+    CompareLogMessages(after_clear_messages, flush_after_clear_result.messages);
+}
+
+TEST_P(LogBufferTest, tail100_nonblocking_1000total) {
+    auto log_messages = GenerateRandomLogMessages(1000);
+    LogMessages(log_messages);
+
+    constexpr int kTailCount = 100;
+    std::vector<LogMessage> expected_log_messages{log_messages.end() - kTailCount,
+                                                  log_messages.end()};
+    auto read_log_messages = ReadLogMessagesNonBlockingThread({.tail = kTailCount});
+    CompareLogMessages(expected_log_messages, read_log_messages);
+}
+
+TEST_P(LogBufferTest, tail100_blocking_1000total_then1000more) {
+    auto log_messages = GenerateRandomLogMessages(1000);
+    LogMessages(log_messages);
+
+    constexpr int kTailCount = 100;
+    auto blocking_reader = TestReaderThread({.non_block = false, .tail = kTailCount}, *this);
+
+    std::vector<LogMessage> expected_log_messages{log_messages.end() - kTailCount,
+                                                  log_messages.end()};
+
+    // Wait for the reader to have read the messages.
+    int retry_count = 1s / 5000us;
+    while (retry_count--) {
+        usleep(5000);
         auto lock = std::lock_guard{logd_lock};
-        std::unique_ptr<LogWriter> test_writer(
-                new TestWriter(&read_log_messages_after_clear, nullptr));
-        std::unique_ptr<FlushToState> flush_to_state =
-                log_buffer_->CreateFlushToState(1, kLogMaskAll);
-        EXPECT_TRUE(log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr));
-        EXPECT_EQ(7ULL, flush_to_state->start());
+        if (blocking_reader.read_log_messages().size() == expected_log_messages.size()) {
+            CompareLogMessages(expected_log_messages, blocking_reader.read_log_messages());
+            break;
+        }
     }
-    CompareLogMessages(after_clear_messages, read_log_messages_after_clear);
+    ASSERT_GT(retry_count, 0);
+
+    // Log more messages
+    log_messages = GenerateRandomLogMessages(1000);
+    LogMessages(log_messages);
+    expected_log_messages.insert(expected_log_messages.end(), log_messages.begin(),
+                                 log_messages.end());
+
+    // Wait for the reader to have read the new messages.
+    retry_count = 1s / 5000us;
+    while (retry_count--) {
+        usleep(5000);
+        auto lock = std::lock_guard{logd_lock};
+        if (blocking_reader.read_log_messages().size() == expected_log_messages.size()) {
+            CompareLogMessages(expected_log_messages, blocking_reader.read_log_messages());
+            break;
+        }
+    }
+    ASSERT_GT(retry_count, 0);
+
+    ReleaseAndJoinReaders();
+
+    // Final check that no extraneous logs were logged.
+    CompareLogMessages(expected_log_messages, blocking_reader.read_log_messages());
+}
+
+TEST_P(LogBufferTest, tail100_nonblocking_50total) {
+    auto log_messages = GenerateRandomLogMessages(50);
+    LogMessages(log_messages);
+
+    constexpr int kTailCount = 100;
+    auto read_log_messages = ReadLogMessagesNonBlockingThread({.tail = kTailCount});
+    CompareLogMessages(log_messages, read_log_messages);
+}
+
+TEST_P(LogBufferTest, tail100_blocking_50total_then1000more) {
+    auto log_messages = GenerateRandomLogMessages(50);
+    LogMessages(log_messages);
+
+    constexpr int kTailCount = 100;
+    auto blocking_reader = TestReaderThread({.non_block = false, .tail = kTailCount}, *this);
+
+    std::vector<LogMessage> expected_log_messages = log_messages;
+
+    // Wait for the reader to have read the messages.
+    int retry_count = 1s / 5000us;
+    while (retry_count--) {
+        usleep(5000);
+        auto lock = std::lock_guard{logd_lock};
+        if (blocking_reader.read_log_messages().size() == expected_log_messages.size()) {
+            CompareLogMessages(expected_log_messages, blocking_reader.read_log_messages());
+            break;
+        }
+    }
+    ASSERT_GT(retry_count, 0);
+
+    // Log more messages
+    log_messages = GenerateRandomLogMessages(1000);
+    LogMessages(log_messages);
+    expected_log_messages.insert(expected_log_messages.end(), log_messages.begin(),
+                                 log_messages.end());
+
+    // Wait for the reader to have read the new messages.
+    retry_count = 1s / 5000us;
+    while (retry_count--) {
+        usleep(5000);
+        auto lock = std::lock_guard{logd_lock};
+        if (blocking_reader.read_log_messages().size() == expected_log_messages.size()) {
+            CompareLogMessages(expected_log_messages, blocking_reader.read_log_messages());
+
+            break;
+        }
+    }
+    ASSERT_GT(retry_count, 0);
+
+    ReleaseAndJoinReaders();
+
+    // Final check that no extraneous logs were logged.
+    CompareLogMessages(expected_log_messages, blocking_reader.read_log_messages());
 }
 
 INSTANTIATE_TEST_CASE_P(LogBufferTests, LogBufferTest,
diff --git a/logd/LogBufferTest.h b/logd/LogBufferTest.h
index eeeb980..0d42bd3 100644
--- a/logd/LogBufferTest.h
+++ b/logd/LogBufferTest.h
@@ -16,12 +16,14 @@
 
 #pragma once
 
+#include <chrono>
 #include <string>
 #include <vector>
 
 #include <gtest/gtest.h>
 
 #include "ChattyLogBuffer.h"
+#include "LogBuffer.h"
 #include "LogReaderList.h"
 #include "LogStatistics.h"
 #include "LogTags.h"
@@ -29,6 +31,8 @@
 #include "SerializedLogBuffer.h"
 #include "SimpleLogBuffer.h"
 
+using namespace std::chrono_literals;
+
 struct LogMessage {
     logger_entry entry;
     std::string message;
@@ -81,11 +85,95 @@
 
     void LogMessages(const std::vector<LogMessage>& messages) {
         for (auto& [entry, message, _] : messages) {
-            log_buffer_->Log(static_cast<log_id_t>(entry.lid), log_time(entry.sec, entry.nsec),
-                             entry.uid, entry.pid, entry.tid, message.c_str(), message.size());
+            EXPECT_GT(log_buffer_->Log(static_cast<log_id_t>(entry.lid),
+                                       log_time(entry.sec, entry.nsec), entry.uid, entry.pid,
+                                       entry.tid, message.c_str(), message.size()),
+                      0);
         }
     }
 
+    struct FlushMessagesResult {
+        std::vector<LogMessage> messages;
+        uint64_t next_sequence;
+    };
+
+    FlushMessagesResult FlushMessages(uint64_t sequence = 1, LogMask log_mask = kLogMaskAll) {
+        std::vector<LogMessage> read_log_messages;
+        auto lock = std::lock_guard{logd_lock};
+        std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
+
+        auto flush_to_state = log_buffer_->CreateFlushToState(sequence, log_mask);
+        EXPECT_TRUE(log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr));
+        return {read_log_messages, flush_to_state->start()};
+    }
+
+    struct ReaderThreadParams {
+        bool non_block = true;
+        unsigned long tail = 0;
+        LogMask log_mask = kLogMaskAll;
+        pid_t pid = 0;
+        log_time start_time = {};
+        uint64_t sequence = 1;
+        std::chrono::steady_clock::time_point deadline = {};
+    };
+
+    class TestReaderThread {
+      public:
+        TestReaderThread(const ReaderThreadParams& params, LogBufferTest& test) : test_(test) {
+            auto lock = std::lock_guard{logd_lock};
+            std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages_, &released_));
+            std::unique_ptr<LogReaderThread> log_reader(new LogReaderThread(
+                    test_.log_buffer_.get(), &test_.reader_list_, std::move(test_writer),
+                    params.non_block, params.tail, params.log_mask, params.pid, params.start_time,
+                    params.sequence, params.deadline));
+            test_.reader_list_.reader_threads().emplace_back(std::move(log_reader));
+        }
+
+        void WaitUntilReleased() {
+            while (!released_) {
+                usleep(5000);
+            }
+        }
+
+        std::vector<LogMessage> read_log_messages() const { return read_log_messages_; }
+
+        LogBufferTest& test_;
+        std::vector<LogMessage> read_log_messages_;
+        bool released_ = false;
+    };
+
+    std::vector<LogMessage> ReadLogMessagesNonBlockingThread(const ReaderThreadParams& params) {
+        EXPECT_TRUE(params.non_block)
+                << "params.non_block must be true for ReadLogMessagesNonBlockingThread()";
+
+        auto reader = TestReaderThread(params, *this);
+        reader.WaitUntilReleased();
+        auto lock = std::lock_guard{logd_lock};
+        EXPECT_EQ(0U, reader_list_.reader_threads().size());
+
+        return reader.read_log_messages();
+    }
+
+    void ReleaseAndJoinReaders() {
+        {
+            auto lock = std::lock_guard{logd_lock};
+            for (auto& reader : reader_list_.reader_threads()) {
+                reader->Release();
+            }
+        }
+
+        auto retries = 1s / 5000us;
+        while (retries--) {
+            usleep(5000);
+            auto lock = std::lock_guard{logd_lock};
+            if (reader_list_.reader_threads().size() == 0) {
+                return;
+            }
+        }
+
+        FAIL() << "ReleaseAndJoinReaders() timed out with reader threads still running";
+    }
+
     LogReaderList reader_list_;
     LogTags tags_;
     PruneList prune_;
diff --git a/logd/LogPermissions.cpp b/logd/LogPermissions.cpp
index 3fd0ea1..63a6d3b 100644
--- a/logd/LogPermissions.cpp
+++ b/logd/LogPermissions.cpp
@@ -20,22 +20,12 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/socket.h>
+
+#include <vector>
 
 #include <private/android_filesystem_config.h>
 
-// gets a list of supplementary group IDs associated with
-// the socket peer.  This is implemented by opening
-// /proc/PID/status and look for the "Group:" line.
-//
-// This function introduces races especially since status
-// can change 'shape' while reading, the net result is err
-// on lack of permission.
-//
-// Race-free alternative is to introduce pairs of sockets
-// and threads for each command and reading, one each that
-// has open permissions, and one that has restricted
-// permissions.
-
 static bool groupIsLog(char* buf) {
     char* ptr;
     static const char ws[] = " \n";
@@ -53,12 +43,19 @@
     return false;
 }
 
-bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid) {
-    if ((uid == AID_ROOT) || (uid == AID_SYSTEM) || (uid == AID_LOG)) {
-        return true;
-    }
+static bool UserIsPrivileged(int id) {
+    return id == AID_ROOT || id == AID_SYSTEM || id == AID_LOG;
+}
 
-    if ((gid == AID_ROOT) || (gid == AID_SYSTEM) || (gid == AID_LOG)) {
+// gets a list of supplementary group IDs associated with
+// the socket peer.  This is implemented by opening
+// /proc/PID/status and look for the "Group:" line.
+//
+// This function introduces races especially since status
+// can change 'shape' while reading, the net result is err
+// on lack of permission.
+bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid) {
+    if (UserIsPrivileged(uid) || UserIsPrivileged(gid)) {
         return true;
     }
 
@@ -73,8 +70,7 @@
 
     //
     // Reading /proc/<pid>/status is rife with race conditions. All of /proc
-    // suffers from this and its use should be minimized. However, we have no
-    // choice.
+    // suffers from this and its use should be minimized.
     //
     // Notably the content from one 4KB page to the next 4KB page can be from a
     // change in shape even if we are gracious enough to attempt to read
@@ -137,5 +133,42 @@
 }
 
 bool clientHasLogCredentials(SocketClient* cli) {
-    return clientHasLogCredentials(cli->getUid(), cli->getGid(), cli->getPid());
+    if (UserIsPrivileged(cli->getUid()) || UserIsPrivileged(cli->getGid())) {
+        return true;
+    }
+
+    // Kernel version 4.13 added SO_PEERGROUPS to return the supplemental groups of a peer socket,
+    // so try that first then fallback to the above racy checking of /proc/<pid>/status if the
+    // kernel is too old.  Per
+    // https://source.android.com/devices/architecture/kernel/android-common, the fallback can be
+    // removed no earlier than 2024.
+    auto supplemental_groups = std::vector<gid_t>(16, -1);
+    socklen_t groups_size = supplemental_groups.size() * sizeof(gid_t);
+
+    int result = getsockopt(cli->getSocket(), SOL_SOCKET, SO_PEERGROUPS, supplemental_groups.data(),
+                            &groups_size);
+
+    if (result != 0) {
+        if (errno != ERANGE) {
+            return clientHasLogCredentials(cli->getUid(), cli->getGid(), cli->getPid());
+        }
+
+        supplemental_groups.resize(groups_size / sizeof(gid_t), -1);
+        result = getsockopt(cli->getSocket(), SOL_SOCKET, SO_PEERGROUPS, supplemental_groups.data(),
+                            &groups_size);
+
+        // There is still some error after resizing supplemental_groups, fallback.
+        if (result != 0) {
+            return clientHasLogCredentials(cli->getUid(), cli->getGid(), cli->getPid());
+        }
+    }
+
+    supplemental_groups.resize(groups_size / sizeof(gid_t), -1);
+    for (const auto& gid : supplemental_groups) {
+        if (UserIsPrivileged(gid)) {
+            return true;
+        }
+    }
+
+    return false;
 }
diff --git a/logd/PkgIds.cpp b/logd/PkgIds.cpp
new file mode 100644
index 0000000..2c7c8a6
--- /dev/null
+++ b/logd/PkgIds.cpp
@@ -0,0 +1,178 @@
+/*
+ * 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 <errno.h>
+#include <pthread.h>
+#include <sys/inotify.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <map>
+#include <mutex>
+#include <string>
+#include <thread>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <packagelistparser/packagelistparser.h>
+
+#include "LogUtils.h"
+
+using android::base::unique_fd;
+
+#define PACKAGES_LIST_FILE "/data/system/packages.list"
+
+struct PkgIdMap {
+    bool active = false;
+    std::mutex lock;
+    std::atomic<bool> running = false;
+    std::map<uid_t, std::string> appid_to_package;
+    std::unique_ptr<std::thread> pklp_thread;
+};
+
+static PkgIdMap* gPkgIdMap = new PkgIdMap;
+
+static bool PackageParseCallback(pkg_info* info, void* userdata) {
+    struct PkgIdMap* global = static_cast<struct PkgIdMap*>(userdata);
+    if (info->name != NULL && info->name[0] != '\0') {
+        global->appid_to_package.emplace(info->uid, info->name);
+    }
+    packagelist_free(info);
+    return true;
+}
+
+static bool ReadPackageList(struct PkgIdMap* global) {
+    std::lock_guard<std::mutex> guard(global->lock);
+    global->appid_to_package.clear();
+    bool rc = packagelist_parse(PackageParseCallback, global);
+    LOG(INFO) << "ReadPackageList, total packages: " << global->appid_to_package.size();
+    return rc;
+}
+
+static void WatchPackageList(struct PkgIdMap* global) {
+    struct inotify_event* event;
+    char event_buf[512];
+
+    unique_fd nfd(inotify_init1(IN_CLOEXEC));
+    if (nfd == -1) {
+        LOG(FATAL) << "inotify_init failed";
+        return;
+    }
+
+    global->active = false;
+    while (1) {
+        if (!global->active) {
+            LOG(INFO) << "start watching " PACKAGES_LIST_FILE " ...";
+            int ret = inotify_add_watch(nfd, PACKAGES_LIST_FILE, IN_DELETE_SELF);
+            if (ret == -1) {
+                if (errno == ENOENT || errno == EACCES) {
+                    LOG(INFO) << "missing " PACKAGES_LIST_FILE "; retrying...";
+                    return;
+                } else {
+                    PLOG(ERROR) << "inotify_add_watch failed";
+                    return;
+                }
+            }
+
+            if (ReadPackageList(global) == false) {
+                LOG(ERROR) << "ReadPackageList failed";
+                return;
+            }
+            global->active = true;
+        }
+
+        int event_pos = 0;
+        ssize_t res = TEMP_FAILURE_RETRY(read(nfd, event_buf, sizeof(event_buf)));
+        if (res == -1 || static_cast<size_t>(res) < sizeof(*event)) {
+            LOG(ERROR) << "failed to read inotify event, res: " << res;
+            global->active = false;
+            continue;
+        }
+
+        while (res >= static_cast<ssize_t>(sizeof(*event))) {
+            int event_size;
+            event = reinterpret_cast<struct inotify_event*>(event_buf + event_pos);
+
+            if ((event->mask & IN_IGNORED) == IN_IGNORED) {
+                global->active = false;
+            }
+
+            event_size = sizeof(*event) + event->len;
+            res -= event_size;
+            event_pos += event_size;
+        }
+    }
+}
+
+static void StartHandler(struct PkgIdMap* global) {
+    prctl(PR_SET_NAME, "logd.pkglist");
+    WatchPackageList(global);
+    global->running = false;
+}
+
+void StartPkgMonitor() {
+    std::lock_guard<std::mutex> guard(gPkgIdMap->lock);
+    if (gPkgIdMap->pklp_thread.get() == nullptr) {
+        gPkgIdMap->running = true;
+        gPkgIdMap->pklp_thread = std::make_unique<std::thread>(StartHandler, gPkgIdMap);
+    } else if (!gPkgIdMap->running) {
+        gPkgIdMap->pklp_thread->join();
+        gPkgIdMap->running = true;
+        gPkgIdMap->pklp_thread.reset(new std::thread(StartHandler, gPkgIdMap));
+    }
+}
+
+char* android::uidToName(uid_t u) {
+    {
+        std::lock_guard<std::mutex> guard(gPkgIdMap->lock);
+        if (gPkgIdMap->active) {
+            const auto& iter = gPkgIdMap->appid_to_package.find(u);
+            if (iter != gPkgIdMap->appid_to_package.end()) {
+                return strdup(iter->second.c_str());
+            }
+        }
+    }
+
+    struct Userdata {
+        uid_t uid;
+        char* name;
+    } userdata = {
+            .uid = u,
+            .name = nullptr,
+    };
+
+    packagelist_parse(
+            [](pkg_info* info, void* callback_parameter) {
+                auto userdata = reinterpret_cast<Userdata*>(callback_parameter);
+                bool result = true;
+                if (info->uid == userdata->uid) {
+                    userdata->name = strdup(info->name);
+                    // false to stop processing
+                    result = false;
+                }
+                packagelist_free(info);
+                return result;
+            },
+            &userdata);
+
+    if (userdata.name != nullptr) {
+        StartPkgMonitor();
+    }
+    return userdata.name;
+}
diff --git a/logd/SerializedFlushToState.cpp b/logd/SerializedFlushToState.cpp
index 30276ff..a514772 100644
--- a/logd/SerializedFlushToState.cpp
+++ b/logd/SerializedFlushToState.cpp
@@ -34,7 +34,7 @@
 SerializedFlushToState::~SerializedFlushToState() {
     log_id_for_each(i) {
         if (log_positions_[i]) {
-            log_positions_[i]->buffer_it->DecReaderRefCount();
+            log_positions_[i]->buffer_it->DetachReader(this);
         }
     }
 }
@@ -49,7 +49,7 @@
     if (it == logs_[log_id].end()) {
         --it;
     }
-    it->IncReaderRefCount();
+    it->AttachReader(this);
     log_position.buffer_it = it;
 
     // Find the offset of the first log with sequence number >= start().
@@ -80,9 +80,9 @@
             logs_needed_from_next_position_[log_id] = true;
         } else {
             // Otherwise, if there is another buffer piece, move to that and do the same check.
-            buffer_it->DecReaderRefCount();
+            buffer_it->DetachReader(this);
             ++buffer_it;
-            buffer_it->IncReaderRefCount();
+            buffer_it->AttachReader(this);
             log_positions_[log_id]->read_offset = 0;
             if (buffer_it->write_offset() == 0) {
                 logs_needed_from_next_position_[log_id] = true;
@@ -145,15 +145,11 @@
     return {log_id, entry};
 }
 
-void SerializedFlushToState::Prune(log_id_t log_id,
-                                   const std::list<SerializedLogChunk>::iterator& buffer_it) {
-    // If we don't have a position for this log or if we're not referencing buffer_it, ignore.
-    if (!log_positions_[log_id].has_value() || log_positions_[log_id]->buffer_it != buffer_it) {
-        return;
-    }
+void SerializedFlushToState::Prune(log_id_t log_id) {
+    CHECK(log_positions_[log_id].has_value());
 
     // Decrease the ref count since we're deleting our reference.
-    buffer_it->DecReaderRefCount();
+    log_positions_[log_id]->buffer_it->DetachReader(this);
 
     // Delete in the reference.
     log_positions_[log_id].reset();
diff --git a/logd/SerializedFlushToState.h b/logd/SerializedFlushToState.h
index 1a69f7b..8a0d0c5 100644
--- a/logd/SerializedFlushToState.h
+++ b/logd/SerializedFlushToState.h
@@ -61,8 +61,7 @@
 
     // If the parent log buffer prunes logs, the reference that this class contains may become
     // invalid, so this must be called first to drop the reference to buffer_it, if any.
-    void Prune(log_id_t log_id, const std::list<SerializedLogChunk>::iterator& buffer_it)
-            REQUIRES(logd_lock);
+    void Prune(log_id_t log_id) REQUIRES(logd_lock);
 
   private:
     // Set logs_needed_from_next_position_[i] to indicate if log_positions_[i] points to an unread
diff --git a/logd/SerializedFlushToStateTest.cpp b/logd/SerializedFlushToStateTest.cpp
index f41de37..12904cb 100644
--- a/logd/SerializedFlushToStateTest.cpp
+++ b/logd/SerializedFlushToStateTest.cpp
@@ -306,5 +306,5 @@
     auto state = SerializedFlushToState{1, kLogMaskAll, log_chunks};
     ASSERT_TRUE(state.HasUnreadLogs());
 
-    state.Prune(LOG_ID_MAIN, log_chunks[LOG_ID_MAIN].begin());
+    state.Prune(LOG_ID_MAIN);
 }
diff --git a/logd/SerializedLogBuffer.cpp b/logd/SerializedLogBuffer.cpp
index aa80864..7446813 100644
--- a/logd/SerializedLogBuffer.cpp
+++ b/logd/SerializedLogBuffer.cpp
@@ -131,14 +131,6 @@
     chunk.DecReaderRefCount();
 }
 
-void SerializedLogBuffer::NotifyReadersOfPrune(
-        log_id_t log_id, const std::list<SerializedLogChunk>::iterator& chunk) {
-    for (const auto& reader_thread : reader_list_->reader_threads()) {
-        auto& state = reinterpret_cast<SerializedFlushToState&>(reader_thread->flush_to_state());
-        state.Prune(log_id, chunk);
-    }
-}
-
 void SerializedLogBuffer::Prune(log_id_t log_id, size_t bytes_to_free, uid_t uid) {
     auto& log_buffer = logs_[log_id];
     auto it = log_buffer.begin();
@@ -174,7 +166,7 @@
 
         // Readers may have a reference to the chunk to track their last read log_position.
         // Notify them to delete the reference.
-        NotifyReadersOfPrune(log_id, it_to_prune);
+        it_to_prune->NotifyReadersOfPrune(log_id);
 
         if (uid != 0) {
             // Reorder the log buffer to remove logs from the given UID.  If there are no logs left
diff --git a/logd/SerializedLogBuffer.h b/logd/SerializedLogBuffer.h
index 239a81e..4ce7a3b 100644
--- a/logd/SerializedLogBuffer.h
+++ b/logd/SerializedLogBuffer.h
@@ -58,8 +58,6 @@
     bool ShouldLog(log_id_t log_id, const char* msg, uint16_t len);
     void MaybePrune(log_id_t log_id) REQUIRES(logd_lock);
     void Prune(log_id_t log_id, size_t bytes_to_free, uid_t uid) REQUIRES(logd_lock);
-    void NotifyReadersOfPrune(log_id_t log_id, const std::list<SerializedLogChunk>::iterator& chunk)
-            REQUIRES(logd_lock);
     void RemoveChunkFromStats(log_id_t log_id, SerializedLogChunk& chunk);
     size_t GetSizeUsed(log_id_t id) REQUIRES(logd_lock);
 
diff --git a/logd/SerializedLogChunk.cpp b/logd/SerializedLogChunk.cpp
index 1ffe7a8..a512bf3 100644
--- a/logd/SerializedLogChunk.cpp
+++ b/logd/SerializedLogChunk.cpp
@@ -19,6 +19,7 @@
 #include <android-base/logging.h>
 
 #include "CompressionEngine.h"
+#include "SerializedFlushToState.h"
 
 SerializedLogChunk::~SerializedLogChunk() {
     CHECK_EQ(reader_ref_count_, 0U);
@@ -52,6 +53,26 @@
     }
 }
 
+void SerializedLogChunk::AttachReader(SerializedFlushToState* reader) {
+    readers_.emplace_back(reader);
+    IncReaderRefCount();
+}
+
+void SerializedLogChunk::DetachReader(SerializedFlushToState* reader) {
+    auto it = std::find(readers_.begin(), readers_.end(), reader);
+    CHECK(readers_.end() != it);
+    readers_.erase(it);
+    DecReaderRefCount();
+}
+
+void SerializedLogChunk::NotifyReadersOfPrune(log_id_t log_id) {
+    // Readers will call DetachReader() in their Prune() call, so we make a copy of the list first.
+    auto readers = readers_;
+    for (auto& reader : readers) {
+        reader->Prune(log_id);
+    }
+}
+
 bool SerializedLogChunk::ClearUidLogs(uid_t uid, log_id_t log_id, LogStatistics* stats) {
     CHECK_EQ(reader_ref_count_, 0U);
     if (write_offset_ == 0) {
diff --git a/logd/SerializedLogChunk.h b/logd/SerializedLogChunk.h
index 645433d..2318a2d 100644
--- a/logd/SerializedLogChunk.h
+++ b/logd/SerializedLogChunk.h
@@ -18,12 +18,17 @@
 
 #include <sys/types.h>
 
+#include <vector>
+
 #include <android-base/logging.h>
 
 #include "LogWriter.h"
+#include "LogdLock.h"
 #include "SerializedData.h"
 #include "SerializedLogEntry.h"
 
+class SerializedFlushToState;
+
 class SerializedLogChunk {
   public:
     explicit SerializedLogChunk(size_t size) : contents_(size) {}
@@ -33,6 +38,10 @@
     void Compress();
     void IncReaderRefCount();
     void DecReaderRefCount();
+    void AttachReader(SerializedFlushToState* reader);
+    void DetachReader(SerializedFlushToState* reader);
+
+    void NotifyReadersOfPrune(log_id_t log_id) REQUIRES(logd_lock);
 
     // Must have no readers referencing this.  Return true if there are no logs left in this chunk.
     bool ClearUidLogs(uid_t uid, log_id_t log_id, LogStatistics* stats);
@@ -76,4 +85,5 @@
     bool writer_active_ = true;
     uint64_t highest_sequence_number_ = 1;
     SerializedData compressed_log_;
+    std::vector<SerializedFlushToState*> readers_;
 };
diff --git a/logd/AndroidTest.xml b/logd/device_test_config.xml
similarity index 100%
rename from logd/AndroidTest.xml
rename to logd/device_test_config.xml
diff --git a/logd/logd_test.cpp b/logd/logd_test.cpp
index 828f580..ec09cd7 100644
--- a/logd/logd_test.cpp
+++ b/logd/logd_test.cpp
@@ -32,6 +32,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
+#include <gtest/gtest-death-test.h>
 #include <gtest/gtest.h>
 #include <log/log_read.h>
 #include <private/android_filesystem_config.h>
@@ -843,9 +844,9 @@
 TEST(logd, no_epipe) {
 #ifdef __ANDROID__
     // Actually generating SIGPIPE in logd is racy, since we need to close the socket quicker than
-    // logd finishes writing the data to it, so we try 10 times, which should be enough to trigger
+    // logd finishes writing the data to it, so we try 5 times, which should be enough to trigger
     // SIGPIPE if logd isn't ignoring SIGPIPE
-    for (int i = 0; i < 10; ++i) {
+    for (int i = 0; i < 5; ++i) {
         unique_fd sock1(
                 socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM));
         ASSERT_GT(sock1, 0);
@@ -861,7 +862,7 @@
 
         struct pollfd p = {.fd = sock2, .events = POLLIN, .revents = 0};
 
-        int ret = poll(&p, 1, 20);
+        int ret = poll(&p, 1, 1000);
         EXPECT_EQ(ret, 1);
         EXPECT_TRUE(p.revents & POLLIN);
         EXPECT_FALSE(p.revents & POLL_ERR);
@@ -870,3 +871,39 @@
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
+
+// Only AID_ROOT, AID_SYSTEM, and AID_LOG can set log sizes.  Ensure that a different user, AID_BIN,
+// cannot set the log size.
+TEST(logd, logging_permissions) {
+#ifdef __ANDROID__
+    if (getuid() != 0) {
+        GTEST_SKIP() << "This test requires root";
+    }
+
+    auto child_main = [] {
+        setgroups(0, nullptr);
+        setgid(AID_BIN);
+        setuid(AID_BIN);
+
+        std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
+                android_logger_list_alloc(0, 0, 0), &android_logger_list_free};
+        if (!logger_list) {
+            _exit(1);
+        }
+        auto logger = android_logger_open(logger_list.get(), LOG_ID_MAIN);
+        if (!logger) {
+            _exit(2);
+        }
+        // This line should fail, so if it returns 0, we exit with an error.
+        if (android_logger_set_log_size(logger, 2 * 1024 * 1024) == 0) {
+            _exit(3);
+        }
+        _exit(EXIT_SUCCESS);
+    };
+
+    ASSERT_EXIT(child_main(), testing::ExitedWithCode(0), "");
+
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/logd/main.cpp b/logd/main.cpp
index c92c5b7..d0d2ac7 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -43,7 +43,6 @@
 #include <cutils/android_get_control_file.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
-#include <packagelistparser/packagelistparser.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 #include <processgroup/sched_policy.h>
@@ -79,11 +78,6 @@
         PLOG(FATAL) << "failed to set background scheduling policy";
     }
 
-    sched_param param = {};
-    if (sched_setscheduler((pid_t)0, SCHED_BATCH, &param) < 0) {
-        PLOG(FATAL) << "failed to set batch scheduler";
-    }
-
     if (!GetBoolProperty("ro.debuggable", false)) {
         if (prctl(PR_SET_DUMPABLE, 0) == -1) {
             PLOG(FATAL) << "failed to clear PR_SET_DUMPABLE";
@@ -121,32 +115,6 @@
     return GetBoolProperty(name, default_value);
 }
 
-char* android::uidToName(uid_t u) {
-    struct Userdata {
-        uid_t uid;
-        char* name;
-    } userdata = {
-            .uid = u,
-            .name = nullptr,
-    };
-
-    packagelist_parse(
-            [](pkg_info* info, void* callback_parameter) {
-                auto userdata = reinterpret_cast<Userdata*>(callback_parameter);
-                bool result = true;
-                if (info->uid == userdata->uid) {
-                    userdata->name = strdup(info->name);
-                    // false to stop processing
-                    result = false;
-                }
-                packagelist_free(info);
-                return result;
-            },
-            &userdata);
-
-    return userdata.name;
-}
-
 static void readDmesg(LogAudit* al, LogKlog* kl) {
     if (!al && !kl) {
         return;
diff --git a/logwrapper/logwrap.cpp b/logwrapper/logwrap.cpp
index 5a518bc..47d1c22 100644
--- a/logwrapper/logwrap.cpp
+++ b/logwrapper/logwrap.cpp
@@ -161,7 +161,7 @@
     if (log_info->log_target & LOG_ALOG) {
         ALOG(LOG_INFO, log_info->btag, "%s", line);
     }
-    if (log_info->log_target & LOG_FILE) {
+    if (log_info->fp) {
         fprintf(log_info->fp, "%s\n", line);
     }
 }
@@ -338,7 +338,7 @@
     int rc = 0;
     int fd;
 
-    struct log_info log_info;
+    struct log_info log_info = {};
 
     int a = 0;  // start index of unprocessed data
     int b = 0;  // end index of unprocessed data
@@ -510,8 +510,8 @@
 
 err_waitpid:
 err_poll:
-    if (log_target & LOG_FILE) {
-        fclose(log_info.fp); /* Also closes underlying fd */
+    if (log_info.fp) {
+        fclose(log_info.fp);
     }
     if (abbreviated) {
         free_abbr_buf(&log_info.a_buf);