Merge changes Id47b288d,I969565eb,Iba2e654e

* changes:
  logd: drop can_read_security_logs
  logd: create FlushToState class
  logd: fix bug in FlushTo when requesting exact sequence number
diff --git a/logd/ChattyLogBufferTest.cpp b/logd/ChattyLogBufferTest.cpp
index 2e0c947..8754b88 100644
--- a/logd/ChattyLogBufferTest.cpp
+++ b/logd/ChattyLogBufferTest.cpp
@@ -61,7 +61,8 @@
 
     std::vector<LogMessage> read_log_messages;
     std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
-    log_buffer_->FlushTo(test_writer.get(), 1, nullptr, 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"),
@@ -72,12 +73,12 @@
             make_message(5, "test_tag", "not_same"),
             // 3 duplicate logs together print the first, a 1 count chatty message, then the last.
             make_message(6, "test_tag", "duplicate"),
-            make_message(7, "chatty", "uid=0\\([^\\)]+\\) [^ ]+ expire 1 line", true),
+            make_message(7, "chatty", "uid=0\\([^\\)]+\\) [^ ]+ identical 1 line", true),
             make_message(8, "test_tag", "duplicate"),
             make_message(9, "test_tag", "not_same"),
             // 6 duplicate logs together print the first, a 4 count chatty message, then the last.
             make_message(10, "test_tag", "duplicate"),
-            make_message(14, "chatty", "uid=0\\([^\\)]+\\) [^ ]+ expire 4 lines", true),
+            make_message(14, "chatty", "uid=0\\([^\\)]+\\) [^ ]+ identical 4 lines", true),
             make_message(15, "test_tag", "duplicate"),
             make_message(16, "test_tag", "not_same"),
             // duplicate logs > 1 minute apart are not deduplicated.
@@ -117,15 +118,16 @@
 
     std::vector<LogMessage> read_log_messages;
     std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
-    log_buffer_->FlushTo(test_writer.get(), 1, nullptr, 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"),
             make_message(expired_per_chatty_message + 1, "chatty",
-                         "uid=0\\([^\\)]+\\) [^ ]+ expire 65535 lines", true),
+                         "uid=0\\([^\\)]+\\) [^ ]+ identical 65535 lines", true),
             make_message(expired_per_chatty_message + 2, "chatty",
-                         "uid=0\\([^\\)]+\\) [^ ]+ expire 1 line", true),
+                         "uid=0\\([^\\)]+\\) [^ ]+ identical 1 line", true),
             make_message(expired_per_chatty_message + 3, "test_tag", "duplicate"),
             make_message(expired_per_chatty_message + 4, "test_tag", "normal"),
     };
@@ -172,7 +174,8 @@
 
     std::vector<LogMessage> read_log_messages;
     std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
-    log_buffer_->FlushTo(test_writer.get(), 1, nullptr, 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),
@@ -199,4 +202,4 @@
     CompareLogMessages(expected_log_messages, read_log_messages);
 };
 
-INSTANTIATE_TEST_CASE_P(ChattyLogBufferTests, ChattyLogBufferTest, testing::Values("chatty"));
\ No newline at end of file
+INSTANTIATE_TEST_CASE_P(ChattyLogBufferTests, ChattyLogBufferTest, testing::Values("chatty"));
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 859d740..a3ac683 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -25,6 +25,27 @@
 
 #include "LogWriter.h"
 
+// A mask to represent which log buffers a reader is watching, values are (1 << LOG_ID_MAIN), etc.
+using LogMask = uint32_t;
+constexpr uint32_t kLogMaskAll = 0xFFFFFFFF;
+
+// State that a LogBuffer may want to persist across calls to FlushTo().
+class FlushToState {
+  public:
+    FlushToState(uint64_t start, LogMask log_mask) : start_(start), log_mask_(log_mask) {}
+    virtual ~FlushToState() {}
+
+    uint64_t start() const { return start_; }
+    void set_start(uint64_t start) { start_ = start; }
+
+    LogMask log_mask() const { return log_mask_; }
+
+  private:
+    uint64_t start_;
+    LogMask log_mask_;
+};
+
+// Enum for the return values of the `filter` function passed to FlushTo().
 enum class FilterResult {
     kSkip,
     kStop,
@@ -39,19 +60,16 @@
 
     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)
-    static const uint64_t FLUSH_ERROR = 0;
-    virtual uint64_t FlushTo(LogWriter* writer, uint64_t start,
-                             pid_t* last_tid,  // nullable
-                             const std::function<FilterResult(log_id_t log_id, pid_t pid,
-                                                              uint64_t sequence, log_time realtime,
-                                                              uint16_t dropped_count)>& filter) = 0;
+
+    virtual std::unique_ptr<FlushToState> CreateFlushToState(uint64_t start, LogMask log_mask) = 0;
+    virtual bool FlushTo(LogWriter* writer, FlushToState& state,
+                         const std::function<FilterResult(log_id_t log_id, pid_t pid,
+                                                          uint64_t sequence, log_time realtime,
+                                                          uint16_t dropped_count)>& filter) = 0;
 
     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;
 
     virtual uint64_t sequence() const = 0;
-};
\ No newline at end of file
+};
diff --git a/logd/LogBufferTest.cpp b/logd/LogBufferTest.cpp
index 334d57b..bc01c80 100644
--- a/logd/LogBufferTest.cpp
+++ b/logd/LogBufferTest.cpp
@@ -208,8 +208,9 @@
 
     std::vector<LogMessage> read_log_messages;
     std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
-    uint64_t flush_result = log_buffer_->FlushTo(test_writer.get(), 1, nullptr, nullptr);
-    EXPECT_EQ(1ULL, flush_result);
+    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);
 }
 
@@ -335,4 +336,39 @@
     CompareLogMessages(log_messages, read_log_messages);
 }
 
+TEST_P(LogBufferTest, read_last_sequence) {
+    std::vector<LogMessage> log_messages = {
+            {{.pid = 1, .tid = 2, .sec = 10000, .nsec = 20001, .lid = LOG_ID_MAIN, .uid = 0},
+             "first"},
+            {{.pid = 10, .tid = 2, .sec = 10000, .nsec = 20002, .lid = LOG_ID_MAIN, .uid = 0},
+             "second"},
+            {{.pid = 100, .tid = 2, .sec = 10000, .nsec = 20003, .lid = LOG_ID_MAIN, .uid = 0},
+             "third"},
+    };
+    FixupMessages(&log_messages);
+    LogMessages(log_messages);
+
+    std::vector<LogMessage> read_log_messages;
+    bool released = false;
+
+    {
+        auto lock = std::unique_lock{reader_list_.reader_threads_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, 0, {}, 3, {}));
+        reader_list_.reader_threads().emplace_back(std::move(log_reader));
+    }
+
+    while (!released) {
+        usleep(5000);
+    }
+    {
+        auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+        EXPECT_EQ(0U, reader_list_.reader_threads().size());
+    }
+    std::vector<LogMessage> expected_log_messages = {log_messages.back()};
+    CompareLogMessages(expected_log_messages, read_log_messages);
+}
+
 INSTANTIATE_TEST_CASE_P(LogBufferTests, LogBufferTest, testing::Values("chatty", "simple"));
diff --git a/logd/LogBufferTest.h b/logd/LogBufferTest.h
index 1659f12..f91a1b5 100644
--- a/logd/LogBufferTest.h
+++ b/logd/LogBufferTest.h
@@ -45,7 +45,7 @@
 class TestWriter : public LogWriter {
   public:
     TestWriter(std::vector<LogMessage>* msgs, bool* released)
-        : LogWriter(0, true, true), msgs_(msgs), released_(released) {}
+        : LogWriter(0, true), msgs_(msgs), released_(released) {}
     bool Write(const logger_entry& entry, const char* message) override {
         msgs_->emplace_back(LogMessage{entry, std::string(message, entry.len), false});
         return true;
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 35c46aa..44bafb9 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -45,11 +45,8 @@
 
 class SocketLogWriter : public LogWriter {
   public:
-    SocketLogWriter(LogReader* reader, SocketClient* client, bool privileged,
-                    bool can_read_security_logs)
-        : LogWriter(client->getUid(), privileged, can_read_security_logs),
-          reader_(reader),
-          client_(client) {}
+    SocketLogWriter(LogReader* reader, SocketClient* client, bool privileged)
+        : LogWriter(client->getUid(), privileged), reader_(reader), client_(client) {}
 
     bool Write(const logger_entry& entry, const char* msg) override {
         struct iovec iovec[2];
@@ -162,25 +159,23 @@
 
     bool privileged = clientHasLogCredentials(cli);
     bool can_read_security = CanReadSecurityLogs(cli);
+    if (!can_read_security) {
+        logMask &= ~(1 << LOG_ID_SECURITY);
+    }
 
-    std::unique_ptr<LogWriter> socket_log_writer(
-            new SocketLogWriter(this, cli, privileged, can_read_security));
+    std::unique_ptr<LogWriter> socket_log_writer(new SocketLogWriter(this, cli, privileged));
 
     uint64_t sequence = 1;
     // Convert realtime to sequence number
     if (start != log_time::EPOCH) {
         bool start_time_set = false;
         uint64_t last = sequence;
-        auto log_find_start = [pid, logMask, start, &sequence, &start_time_set, &last](
-                                      log_id_t element_log_id, pid_t element_pid,
-                                      uint64_t element_sequence, log_time element_realtime,
-                                      uint16_t) -> FilterResult {
+        auto log_find_start = [pid, start, &sequence, &start_time_set, &last](
+                                      log_id_t, pid_t element_pid, uint64_t element_sequence,
+                                      log_time element_realtime, uint16_t) -> FilterResult {
             if (pid && pid != element_pid) {
                 return FilterResult::kSkip;
             }
-            if ((logMask & (1 << element_log_id)) == 0) {
-                return FilterResult::kSkip;
-            }
             if (start == element_realtime) {
                 sequence = element_sequence;
                 start_time_set = true;
@@ -195,8 +190,8 @@
             }
             return FilterResult::kSkip;
         };
-
-        log_buffer_->FlushTo(socket_log_writer.get(), sequence, nullptr, log_find_start);
+        auto flush_to_state = log_buffer_->CreateFlushToState(sequence, logMask);
+        log_buffer_->FlushTo(socket_log_writer.get(), *flush_to_state, log_find_start);
 
         if (!start_time_set) {
             if (nonBlock) {
diff --git a/logd/LogReaderList.cpp b/logd/LogReaderList.cpp
index 220027b..32ba291 100644
--- a/logd/LogReaderList.cpp
+++ b/logd/LogReaderList.cpp
@@ -18,7 +18,7 @@
 
 // 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 {
+void LogReaderList::NotifyNewLog(LogMask log_mask) const {
     auto lock = std::lock_guard{reader_threads_lock_};
 
     for (const auto& entry : reader_threads_) {
diff --git a/logd/LogReaderList.h b/logd/LogReaderList.h
index 0d84aba..594716a 100644
--- a/logd/LogReaderList.h
+++ b/logd/LogReaderList.h
@@ -20,11 +20,12 @@
 #include <memory>
 #include <mutex>
 
+#include "LogBuffer.h"
 #include "LogReaderThread.h"
 
 class LogReaderList {
   public:
-    void NotifyNewLog(unsigned int log_mask) const;
+    void NotifyNewLog(LogMask log_mask) const;
 
     std::list<std::unique_ptr<LogReaderThread>>& reader_threads() { return reader_threads_; }
     std::mutex& reader_threads_lock() { return reader_threads_lock_; }
diff --git a/logd/LogReaderThread.cpp b/logd/LogReaderThread.cpp
index 3a83f3f..c6e60fe 100644
--- a/logd/LogReaderThread.cpp
+++ b/logd/LogReaderThread.cpp
@@ -29,24 +29,22 @@
 
 LogReaderThread::LogReaderThread(LogBuffer* log_buffer, LogReaderList* reader_list,
                                  std::unique_ptr<LogWriter> writer, bool non_block,
-                                 unsigned long tail, unsigned int log_mask, pid_t pid,
+                                 unsigned long tail, LogMask log_mask, pid_t pid,
                                  log_time start_time, uint64_t start,
                                  std::chrono::steady_clock::time_point deadline)
     : log_buffer_(log_buffer),
       reader_list_(reader_list),
       writer_(std::move(writer)),
       leading_dropped_(false),
-      log_mask_(log_mask),
       pid_(pid),
       tail_(tail),
       count_(0),
       index_(0),
       start_time_(start_time),
-      start_(start),
       deadline_(deadline),
       non_block_(non_block) {
-    memset(last_tid_, 0, sizeof(last_tid_));
     cleanSkip_Locked();
+    flush_to_state_ = log_buffer_->CreateFlushToState(start, log_mask);
     auto thread = std::thread{&LogReaderThread::ThreadFunction, this};
     thread.detach();
 }
@@ -58,8 +56,6 @@
 
     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_) ==
@@ -74,7 +70,9 @@
         lock.unlock();
 
         if (tail_) {
-            log_buffer_->FlushTo(writer_.get(), start, nullptr,
+            auto first_pass_state = log_buffer_->CreateFlushToState(flush_to_state_->start(),
+                                                                    flush_to_state_->log_mask());
+            log_buffer_->FlushTo(writer_.get(), *first_pass_state,
                                  [this](log_id_t log_id, pid_t pid, uint64_t sequence,
                                         log_time realtime, uint16_t dropped_count) {
                                      return FilterFirstPass(log_id, pid, sequence, realtime,
@@ -84,12 +82,12 @@
                     true;  // TODO: Likely a bug, if leading_dropped_ was not true before calling
                            // flushTo(), then it should not be reset to true after.
         }
-        start = log_buffer_->FlushTo(writer_.get(), start, last_tid_,
-                                     [this](log_id_t log_id, pid_t pid, uint64_t sequence,
-                                            log_time realtime, uint16_t dropped_count) {
-                                         return FilterSecondPass(log_id, pid, sequence, realtime,
-                                                                 dropped_count);
-                                     });
+        bool flush_success = log_buffer_->FlushTo(
+                writer_.get(), *flush_to_state_,
+                [this](log_id_t log_id, pid_t pid, uint64_t sequence, log_time realtime,
+                       uint16_t dropped_count) {
+                    return FilterSecondPass(log_id, pid, sequence, realtime, dropped_count);
+                });
 
         // 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
@@ -102,12 +100,10 @@
 
         lock.lock();
 
-        if (start == LogBuffer::FLUSH_ERROR) {
+        if (!flush_success) {
             break;
         }
 
-        start_ = start + 1;
-
         if (non_block_ || release_) {
             break;
         }
@@ -131,8 +127,8 @@
 }
 
 // A first pass to count the number of elements
-FilterResult LogReaderThread::FilterFirstPass(log_id_t log_id, pid_t pid, uint64_t sequence,
-                                              log_time realtime, uint16_t dropped_count) {
+FilterResult LogReaderThread::FilterFirstPass(log_id_t, pid_t pid, uint64_t, log_time realtime,
+                                              uint16_t dropped_count) {
     auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
 
     if (leading_dropped_) {
@@ -142,12 +138,7 @@
         leading_dropped_ = false;
     }
 
-    if (count_ == 0) {
-        start_ = sequence;
-    }
-
-    if ((!pid_ || pid_ == pid) && IsWatching(log_id) &&
-        (start_time_ == log_time::EPOCH || start_time_ <= realtime)) {
+    if ((!pid_ || pid_ == pid) && (start_time_ == log_time::EPOCH || start_time_ <= realtime)) {
         ++count_;
     }
 
@@ -155,12 +146,10 @@
 }
 
 // A second pass to send the selected elements
-FilterResult LogReaderThread::FilterSecondPass(log_id_t log_id, pid_t pid, uint64_t sequence,
+FilterResult LogReaderThread::FilterSecondPass(log_id_t log_id, pid_t pid, uint64_t,
                                                log_time realtime, uint16_t dropped_count) {
     auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
 
-    start_ = sequence;
-
     if (skip_ahead_[log_id]) {
         skip_ahead_[log_id]--;
         return FilterResult::kSkip;
@@ -178,10 +167,6 @@
         return FilterResult::kStop;
     }
 
-    if (!IsWatching(log_id)) {
-        return FilterResult::kSkip;
-    }
-
     if (pid_ && pid_ != pid) {
         return FilterResult::kSkip;
     }
diff --git a/logd/LogReaderThread.h b/logd/LogReaderThread.h
index ba81063..f288d68 100644
--- a/logd/LogReaderThread.h
+++ b/logd/LogReaderThread.h
@@ -38,7 +38,7 @@
   public:
     LogReaderThread(LogBuffer* log_buffer, LogReaderList* reader_list,
                     std::unique_ptr<LogWriter> writer, bool non_block, unsigned long tail,
-                    unsigned int log_mask, pid_t pid, log_time start_time, uint64_t sequence,
+                    LogMask log_mask, pid_t pid, log_time start_time, uint64_t sequence,
                     std::chrono::steady_clock::time_point deadline);
     void triggerReader_Locked() { thread_triggered_condition_.notify_all(); }
 
@@ -52,11 +52,13 @@
         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; }
+    bool IsWatching(log_id_t id) const { return flush_to_state_->log_mask() & (1 << id); }
+    bool IsWatchingMultiple(LogMask log_mask) const {
+        return flush_to_state_->log_mask() & log_mask;
+    }
 
     std::string name() const { return writer_->name(); }
-    uint64_t start() const { return start_; }
+    uint64_t start() const { return flush_to_state_->start(); }
     std::chrono::steady_clock::time_point deadline() const { return deadline_; }
 
   private:
@@ -78,16 +80,14 @@
     // messages should be ignored.
     bool leading_dropped_;
 
-    // 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];
+    // LogBuffer::FlushTo() needs to store state across subsequent calls.
+    std::unique_ptr<FlushToState> flush_to_state_;
 
     // These next three variables are used for reading only the most recent lines aka `adb logcat
     // -t` / `adb logcat -T`.
@@ -103,8 +103,6 @@
     // 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_;
diff --git a/logd/LogWriter.h b/logd/LogWriter.h
index b6c5b67..d43c604 100644
--- a/logd/LogWriter.h
+++ b/logd/LogWriter.h
@@ -23,8 +23,7 @@
 // An interface for writing logs to a reader.
 class LogWriter {
   public:
-    LogWriter(uid_t uid, bool privileged, bool can_read_security_logs)
-        : uid_(uid), privileged_(privileged), can_read_security_logs_(can_read_security_logs) {}
+    LogWriter(uid_t uid, bool privileged) : uid_(uid), privileged_(privileged) {}
     virtual ~LogWriter() {}
 
     virtual bool Write(const logger_entry& entry, const char* msg) = 0;
@@ -35,12 +34,10 @@
     uid_t uid() const { return uid_; }
 
     bool privileged() const { return privileged_; }
-    bool can_read_security_logs() const { return can_read_security_logs_; }
 
   private:
     uid_t uid_;
 
     // If this writer sees logs from all UIDs or only its own UID.  See clientHasLogCredentials().
     bool privileged_;
-    bool can_read_security_logs_;  // If this writer sees security logs.  See CanReadSecurityLogs().
 };
\ No newline at end of file
diff --git a/logd/SimpleLogBuffer.cpp b/logd/SimpleLogBuffer.cpp
index 8a11b92..aaa74ae 100644
--- a/logd/SimpleLogBuffer.cpp
+++ b/logd/SimpleLogBuffer.cpp
@@ -110,14 +110,34 @@
     reader_list_->NotifyNewLog(1 << log_id);
 }
 
-uint64_t SimpleLogBuffer::FlushTo(
-        LogWriter* writer, uint64_t start, pid_t* last_tid,
+// These extra parameters are only required for chatty, but since they're a no-op for
+// SimpleLogBuffer, it's easier to include them here, then to duplicate FlushTo() for
+// ChattyLogBuffer.
+class ChattyFlushToState : public FlushToState {
+  public:
+    ChattyFlushToState(uint64_t start, LogMask log_mask) : FlushToState(start, log_mask) {}
+
+    pid_t* last_tid() { return last_tid_; }
+
+  private:
+    pid_t last_tid_[LOG_ID_MAX] = {};
+};
+
+std::unique_ptr<FlushToState> SimpleLogBuffer::CreateFlushToState(uint64_t start,
+                                                                  LogMask log_mask) {
+    return std::make_unique<ChattyFlushToState>(start, log_mask);
+}
+
+bool SimpleLogBuffer::FlushTo(
+        LogWriter* writer, FlushToState& abstract_state,
         const std::function<FilterResult(log_id_t log_id, pid_t pid, uint64_t sequence,
                                          log_time realtime, uint16_t dropped_count)>& filter) {
     auto shared_lock = SharedLock{lock_};
 
+    auto& state = reinterpret_cast<ChattyFlushToState&>(abstract_state);
+
     std::list<LogBufferElement>::iterator it;
-    if (start <= 1) {
+    if (state.start() <= 1) {
         // client wants to start from the beginning
         it = logs_.begin();
     } else {
@@ -126,23 +146,25 @@
         for (it = logs_.end(); it != logs_.begin();
              /* do nothing */) {
             --it;
-            if (it->getSequence() <= start) {
+            if (it->getSequence() == state.start()) {
+                break;
+            } else if (it->getSequence() < state.start()) {
                 it++;
                 break;
             }
         }
     }
 
-    uint64_t curr = start;
-
     for (; it != logs_.end(); ++it) {
         LogBufferElement& element = *it;
 
+        state.set_start(element.getSequence());
+
         if (!writer->privileged() && element.getUid() != writer->uid()) {
             continue;
         }
 
-        if (!writer->can_read_security_logs() && element.getLogId() == LOG_ID_SECURITY) {
+        if (((1 << element.getLogId()) & state.log_mask()) == 0) {
             continue;
         }
 
@@ -157,31 +179,24 @@
             }
         }
 
-        bool same_tid = false;
-        if (last_tid) {
-            same_tid = last_tid[element.getLogId()] == element.getTid();
-            // Dropped (chatty) immediately following a valid log from the
-            // same source in the same log buffer indicates we have a
-            // multiple identical squash.  chatty that differs source
-            // is due to spam filter.  chatty to chatty of different
-            // source is also due to spam filter.
-            last_tid[element.getLogId()] =
-                    (element.getDropped() && !same_tid) ? 0 : element.getTid();
-        }
+        bool same_tid = state.last_tid()[element.getLogId()] == element.getTid();
+        // Dropped (chatty) immediately following a valid log from the same source in the same log
+        // buffer indicates we have a multiple identical squash.  chatty that differs source is due
+        // to spam filter.  chatty to chatty of different source is also due to spam filter.
+        state.last_tid()[element.getLogId()] =
+                (element.getDropped() && !same_tid) ? 0 : element.getTid();
 
         shared_lock.unlock();
-
         // We never prune logs equal to or newer than any LogReaderThreads' `start` value, so the
         // `element` pointer is safe here without the lock
-        curr = element.getSequence();
         if (!element.FlushTo(writer, stats_, same_tid)) {
-            return FLUSH_ERROR;
+            return false;
         }
-
         shared_lock.lock_shared();
     }
 
-    return curr;
+    state.set_start(state.start() + 1);
+    return true;
 }
 
 // clear all rows of type "id" from the buffer.
diff --git a/logd/SimpleLogBuffer.h b/logd/SimpleLogBuffer.h
index 72d26b0..a2ab881 100644
--- a/logd/SimpleLogBuffer.h
+++ b/logd/SimpleLogBuffer.h
@@ -35,10 +35,11 @@
 
     int Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg,
             uint16_t len) override;
-    uint64_t FlushTo(LogWriter* writer, uint64_t start, pid_t* lastTid,
-                     const std::function<FilterResult(log_id_t log_id, pid_t pid, uint64_t sequence,
-                                                      log_time realtime, uint16_t dropped_count)>&
-                             filter) override;
+    std::unique_ptr<FlushToState> CreateFlushToState(uint64_t start, LogMask log_mask) override;
+    bool FlushTo(LogWriter* writer, FlushToState& state,
+                 const std::function<FilterResult(log_id_t log_id, pid_t pid, uint64_t sequence,
+                                                  log_time realtime, uint16_t dropped_count)>&
+                         filter) override;
 
     bool Clear(log_id_t id, uid_t uid) override;
     unsigned long GetSize(log_id_t id) override;