Merge "Process write overflow with number of elements" into main
diff --git a/include/fmq/MessageQueueBase.h b/include/fmq/MessageQueueBase.h
index 5a12b5a..8d65108 100644
--- a/include/fmq/MessageQueueBase.h
+++ b/include/fmq/MessageQueueBase.h
@@ -1278,7 +1278,7 @@
          * Half of the buffer will be discarded to make space for fast writers and
          * reduce chance of repeated overflows. The other half is available to read.
          */
-        size_t historyOffset = mDesc->getSize() / 2;
+        size_t historyOffset = getQuantumCount() / 2 * getQuantumSize();
         mReadPtr->store(writePtr - historyOffset, std::memory_order_release);
         hardware::details::logError("Read failed after an overflow. Resetting read pointer.");
         return true;
diff --git a/tests/fmq_unit_tests.cpp b/tests/fmq_unit_tests.cpp
index 6ddec7c..07ed0ce 100644
--- a/tests/fmq_unit_tests.cpp
+++ b/tests/fmq_unit_tests.cpp
@@ -263,6 +263,35 @@
     size_t mNumMessagesMax = 0;
 };
 
+TYPED_TEST_CASE(UnsynchronizedOverflowHistoryTestSingleElement, TwoByteUnsyncTypes);
+
+template <typename T>
+class UnsynchronizedOverflowHistoryTestSingleElement : public TestBase<T> {
+  protected:
+    virtual void TearDown() { delete mQueue; }
+
+    virtual void SetUp() {
+        static constexpr size_t kNumElementsInQueue = 1;
+        static constexpr size_t kPayloadSizeBytes = 2;
+        if (T::Setup == SetupType::SINGLE_FD) {
+            mQueue = new (std::nothrow) typename T::MQType(kNumElementsInQueue);
+        } else {
+            android::base::unique_fd ringbufferFd(::ashmem_create_region(
+                    "UnsyncHistory", kNumElementsInQueue * kPayloadSizeBytes));
+            mQueue = new (std::nothrow)
+                    typename T::MQType(kNumElementsInQueue, false, std::move(ringbufferFd),
+                                       kNumElementsInQueue * kPayloadSizeBytes);
+        }
+        ASSERT_NE(nullptr, mQueue);
+        ASSERT_TRUE(mQueue->isValid());
+        mNumMessagesMax = mQueue->getQuantumCount();
+        ASSERT_EQ(kNumElementsInQueue, mNumMessagesMax);
+    }
+
+    typename T::MQType* mQueue = nullptr;
+    size_t mNumMessagesMax = 0;
+};
+
 template <typename T>
 class BadQueueConfig : public TestBase<T> {};
 
@@ -1433,3 +1462,30 @@
     std::rotate(data.begin(), data.begin() + 1, data.end());
     ASSERT_TRUE(std::equal(readData.rbegin(), readData.rend(), data.rbegin()));
 }
+
+/*
+ * Verifies a queue of a single element will fail a read after a write overflow
+ * and then recover.
+ */
+TYPED_TEST(UnsynchronizedOverflowHistoryTestSingleElement, ReadAfterOverflow) {
+    constexpr uint16_t kValue = 4;
+    std::vector<uint16_t> data = {kValue};
+
+    // single write/read works normally
+    ASSERT_TRUE(this->mQueue->write(&data[0], 1));
+    uint16_t readDataPlaceholder;
+    ASSERT_TRUE(this->mQueue->read(&readDataPlaceholder, 1));
+    EXPECT_EQ(readDataPlaceholder, kValue);
+
+    // Write more data (first element of the same data) to cause a wrap around
+    ASSERT_TRUE(this->mQueue->write(&data[0], 1));
+    ASSERT_TRUE(this->mQueue->write(&data[0], 1));
+
+    // Attempt a read (this should fail due to how UnsynchronizedWrite works)
+    ASSERT_FALSE(this->mQueue->read(&readDataPlaceholder, 1));
+
+    // Subsequent write/reads should work again
+    ASSERT_TRUE(this->mQueue->write(&data[0], 1));
+    ASSERT_TRUE(this->mQueue->read(&readDataPlaceholder, 1));
+    EXPECT_EQ(readDataPlaceholder, kValue);
+}