libgui: Add dequeue/attach timeout

Adds the ability to specify the timeout when dequeueBuffer or
attachBuffer block due to the lack of a free buffer/slot. By default,
these will block indefinitely (which is signified by a timeout of -1).

When a timeout (other than -1) is specified, non-blocking mode is
disabled and the given timeout will be used instead.

Bug: 25196773
Change-Id: I17fdbeebccb7c8d878703d758ac1209608258e61
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index a32a90c..a45a5be 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -535,4 +535,66 @@
     }
 }
 
+TEST_F(BufferQueueTest, TestTimeouts) {
+    createBufferQueue();
+    sp<DummyConsumer> dc(new DummyConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    IGraphicBufferProducer::QueueBufferOutput output;
+    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+            NATIVE_WINDOW_API_CPU, true, &output));
+
+    // Fill up the queue. Since the controlledByApp flags are set to true, this
+    // queue should be in non-blocking mode, and we should be recycling the same
+    // two buffers
+    for (int i = 0; i < 5; ++i) {
+        int slot = BufferQueue::INVALID_BUFFER_SLOT;
+        sp<Fence> fence = Fence::NO_FENCE;
+        auto result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0);
+        if (i < 2) {
+            ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+                    result);
+        } else {
+            ASSERT_EQ(OK, result);
+        }
+        sp<GraphicBuffer> buffer;
+        ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+        IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+                HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+                NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+        IGraphicBufferProducer::QueueBufferOutput output{};
+        ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+    }
+
+    const auto TIMEOUT = ms2ns(250);
+    mProducer->setDequeueTimeout(TIMEOUT);
+
+    // Setting a timeout will change the BufferQueue into blocking mode (with
+    // one droppable buffer in the queue and one free from the previous
+    // dequeue/queues), so dequeue and queue two more buffers: one to replace
+    // the current droppable buffer, and a second to max out the buffer count
+    sp<GraphicBuffer> buffer; // Save a buffer to attach later
+    for (int i = 0; i < 2; ++i) {
+        int slot = BufferQueue::INVALID_BUFFER_SLOT;
+        sp<Fence> fence = Fence::NO_FENCE;
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+        ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+        IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+                HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+                NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+        ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+    }
+
+    int slot = BufferQueue::INVALID_BUFFER_SLOT;
+    sp<Fence> fence = Fence::NO_FENCE;
+    auto startTime = systemTime();
+    ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+    ASSERT_GE(systemTime() - startTime, TIMEOUT);
+
+    // We're technically attaching the same buffer multiple times (since we
+    // queued it previously), but that doesn't matter for this test
+    startTime = systemTime();
+    ASSERT_EQ(TIMED_OUT, mProducer->attachBuffer(&slot, buffer));
+    ASSERT_GE(systemTime() - startTime, TIMEOUT);
+}
+
 } // namespace android