| /* |
| * Copyright (C) 2026 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. |
| */ |
| |
| #define LOG_TAG "Surface2HGBP10Test" |
| |
| #include <gtest/gtest.h> |
| #include <gui/BufferItemConsumer.h> |
| #include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h> |
| #include <gui/bufferqueue/1.0/Surface2HGraphicBufferProducer.h> |
| #include <hardware/gralloc.h> |
| #include <system/window.h> |
| #include <ui/Fence.h> |
| #include <ui/Rect.h> |
| #include <utils/RefBase.h> |
| |
| namespace android { |
| |
| using ::android::hardware::Return; |
| using ::android::hardware::Void; |
| using ::android::hardware::graphics::bufferqueue::V1_0::utils::Surface2HGraphicBufferProducer; |
| using ::android::hardware::media::V1_0::AnwBuffer; |
| |
| using HGraphicBufferProducer = |
| ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer; |
| using HProducerListener = ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener; |
| using HDataSpace = ::android::hardware::graphics::common::V1_0::Dataspace; |
| using HDisconnectMode = |
| ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer::DisconnectMode; |
| |
| const uint64_t kDefaultUsage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN; |
| |
| class Surface2HGraphicBufferProducer10Test : public ::testing::Test { |
| protected: |
| void SetUp() override { |
| sp<Surface> surface; |
| std::tie(mConsumer, surface) = BufferItemConsumer::create(kDefaultUsage); |
| ASSERT_NE(nullptr, mConsumer.get()); |
| ASSERT_NE(nullptr, surface.get()); |
| mHGBP = new Surface2HGraphicBufferProducer(surface); |
| ASSERT_NE(nullptr, mHGBP.get()); |
| } |
| |
| sp<BufferItemConsumer> mConsumer; |
| sp<Surface2HGraphicBufferProducer> mHGBP; |
| |
| // Wrapper functions for Surface2HGraphicBufferProducer methods |
| |
| Return<int32_t> setMaxDequeuedBufferCount(int32_t maxDequeuedBuffers) { |
| return mHGBP->setMaxDequeuedBufferCount(maxDequeuedBuffers); |
| } |
| |
| std::tuple<int, AnwBuffer> requestBuffer(int32_t slot) { |
| int outStatus; |
| AnwBuffer outBuffer; |
| mHGBP->requestBuffer(slot, [&](int status, const AnwBuffer& buffer) { |
| outStatus = status; |
| outBuffer = buffer; |
| }); |
| return {outStatus, outBuffer}; |
| } |
| |
| Return<int32_t> setAsyncMode(bool async) { return mHGBP->setAsyncMode(async); } |
| |
| std::tuple<int, int32_t, ::android::hardware::hidl_handle, |
| HGraphicBufferProducer::FrameEventHistoryDelta> |
| dequeueBuffer(uint32_t width, uint32_t height, |
| ::android::hardware::graphics::common::V1_0::PixelFormat format, uint32_t usage, |
| bool getFrameTimestamps) { |
| int outStatus; |
| int32_t outSlot; |
| ::android::hardware::hidl_handle outFence; |
| HGraphicBufferProducer::FrameEventHistoryDelta outTimestamps; |
| mHGBP->dequeueBuffer(width, height, format, usage, getFrameTimestamps, |
| [&](int status, int32_t slot, |
| const ::android::hardware::hidl_handle& fence, |
| const HGraphicBufferProducer::FrameEventHistoryDelta& timestamps) { |
| outStatus = status; |
| outSlot = slot; |
| outFence = fence; |
| outTimestamps = timestamps; |
| }); |
| return {outStatus, outSlot, outFence, outTimestamps}; |
| } |
| |
| Return<int32_t> detachBuffer(int32_t slot) { return mHGBP->detachBuffer(slot); } |
| |
| std::tuple<int, AnwBuffer, ::android::hardware::hidl_handle> detachNextBuffer() { |
| int outStatus; |
| AnwBuffer outBuffer; |
| ::android::hardware::hidl_handle outFence; |
| mHGBP->detachNextBuffer([&](int status, const AnwBuffer& buffer, |
| const ::android::hardware::hidl_handle& fence) { |
| outStatus = status; |
| outBuffer = buffer; |
| outFence = fence; |
| }); |
| return {outStatus, outBuffer, outFence}; |
| } |
| |
| std::tuple<int, int32_t> attachBuffer(const AnwBuffer& buffer) { |
| int outStatus; |
| int32_t outSlot; |
| mHGBP->attachBuffer(buffer, [&](int32_t status, int32_t slot) { |
| outStatus = status; |
| outSlot = slot; |
| }); |
| return {outStatus, outSlot}; |
| } |
| |
| std::tuple<int, HGraphicBufferProducer::QueueBufferOutput> queueBuffer( |
| int32_t slot, const HGraphicBufferProducer::QueueBufferInput& input) { |
| int outStatus; |
| HGraphicBufferProducer::QueueBufferOutput outOutput; |
| mHGBP->queueBuffer(slot, input, |
| [&](int status, |
| const HGraphicBufferProducer::QueueBufferOutput& output) { |
| outStatus = status; |
| outOutput = output; |
| }); |
| return {outStatus, outOutput}; |
| } |
| |
| Return<int32_t> cancelBuffer(int32_t slot, const ::android::hardware::hidl_handle& fence) { |
| return mHGBP->cancelBuffer(slot, fence); |
| } |
| |
| std::tuple<int32_t, int32_t> query(int32_t what) { |
| int32_t outValue; |
| int32_t outResult; |
| mHGBP->query(what, [&](int32_t result, int32_t value) { |
| outValue = value; |
| outResult = result; |
| }); |
| return {outValue, outResult}; |
| } |
| |
| std::tuple<int, HGraphicBufferProducer::QueueBufferOutput> connect( |
| const sp<HProducerListener>& listener, int32_t api, bool producerControlledByApp) { |
| int outStatus; |
| HGraphicBufferProducer::QueueBufferOutput outOutput; |
| mHGBP->connect(listener, api, producerControlledByApp, |
| [&](int status, const HGraphicBufferProducer::QueueBufferOutput& output) { |
| outStatus = status; |
| outOutput = output; |
| }); |
| return {outStatus, outOutput}; |
| } |
| |
| Return<int32_t> disconnect(int32_t api, HDisconnectMode mode) { |
| return mHGBP->disconnect(api, mode); |
| } |
| |
| Return<void> allocateBuffers(uint32_t width, uint32_t height, |
| ::android::hardware::graphics::common::V1_0::PixelFormat format, |
| uint64_t usage) { |
| return mHGBP->allocateBuffers(width, height, format, usage); |
| } |
| |
| Return<int32_t> allowAllocation(bool allow) { return mHGBP->allowAllocation(allow); } |
| |
| Return<int32_t> setGenerationNumber(uint32_t generationNumber) { |
| return mHGBP->setGenerationNumber(generationNumber); |
| } |
| |
| Return<int32_t> setDequeueTimeout(int64_t timeoutNs) { |
| return mHGBP->setDequeueTimeout(timeoutNs); |
| } |
| |
| Return<uint64_t> getUniqueId() { |
| uint64_t outId = 0; |
| mHGBP->getUniqueId([&](int32_t, uint64_t id) { outId = id; }); |
| return outId; |
| } |
| |
| std::string getConsumerName() { |
| std::string outName; |
| mHGBP->getConsumerName( |
| [&](const ::android::hardware::hidl_string& name) { outName = name; }); |
| return outName; |
| } |
| }; |
| |
| class TestHProducerListener final : public HProducerListener { |
| public: |
| explicit TestHProducerListener() = default; |
| |
| ::android::hardware::Return<bool> needsReleaseNotify() override { return false; } |
| ::android::hardware::Return<void> onBufferReleased() override { return {}; } |
| }; |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Queue_Succeeds) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| EXPECT_EQ(OK, connectStatus); |
| |
| auto [dequeueStatus, slot, fence, timestamps] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| EXPECT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| |
| auto [requestStatus, buffer] = requestBuffer(slot); |
| EXPECT_EQ(OK, requestStatus); |
| |
| HGraphicBufferProducer::QueueBufferInput qInput{}; |
| qInput.timestamp = 12345; |
| qInput.isAutoTimestamp = false; |
| qInput.dataSpace = HDataSpace::UNKNOWN; |
| qInput.crop.left = 0; |
| qInput.crop.top = 0; |
| qInput.crop.right = 1; |
| qInput.crop.bottom = 1; |
| qInput.transform = 0; |
| qInput.fence = ::android::hardware::hidl_handle(); // Empty fence |
| auto [queueBufferStatus, queueBufferOutput] = queueBuffer(slot, qInput); |
| EXPECT_EQ(OK, queueBufferStatus); |
| EXPECT_EQ(1u, queueBufferOutput.width); |
| EXPECT_EQ(1u, queueBufferOutput.height); |
| EXPECT_GE(queueBufferOutput.nextFrameNumber, 1u); |
| |
| BufferItem item; |
| status_t ret = mConsumer->acquireBuffer(&item, 0); |
| EXPECT_EQ(NO_ERROR, ret); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Connect_ReturnsError) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| |
| // Invalid API returns bad value |
| auto [status, output] = connect(listener, 0xDEADBEEF, false); |
| EXPECT_EQ(BAD_VALUE, status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, ConnectAgain_ReturnsError) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| // Can't connect when there is already a producer connected |
| auto [status, output] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| EXPECT_EQ(BAD_VALUE, status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Disconnect_Succeeds) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| int32_t status = disconnect(NATIVE_WINDOW_API_CPU, HDisconnectMode::API); |
| EXPECT_EQ(static_cast<int32_t>(OK), status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Disconnect_ReturnsError) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| // Must disconnect with the same API number used to connect |
| int32_t status = disconnect(NATIVE_WINDOW_API_CPU + 1, HDisconnectMode::API); |
| EXPECT_EQ(static_cast<int32_t>(BAD_VALUE), status); |
| |
| // Disconnecting twice should fail |
| ASSERT_EQ(static_cast<int32_t>(OK), disconnect(NATIVE_WINDOW_API_CPU, HDisconnectMode::API)); |
| status = disconnect(NATIVE_WINDOW_API_CPU, HDisconnectMode::API); |
| EXPECT_EQ(static_cast<int32_t>(NO_INIT), status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Disconnected_DequeueBuffer) { |
| auto [status, slot, fence, timestamps] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| EXPECT_EQ(NO_INIT, status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Disconnected_DetachNextBuffer) { |
| auto [status, buffer, fence] = detachNextBuffer(); |
| EXPECT_EQ(NO_INIT, status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Disconnected_RequestBuffer) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| auto [dequeueStatus, slot, fence, timestamps] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| |
| ASSERT_EQ(static_cast<int32_t>(OK), disconnect(NATIVE_WINDOW_API_CPU, HDisconnectMode::API)); |
| |
| auto [status, buffer] = requestBuffer(slot); |
| EXPECT_EQ(BAD_VALUE, status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Disconnected_DetachBuffer) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| auto [dequeueStatus, slot, fence, timestamps] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| |
| auto [requestStatus, buffer] = requestBuffer(slot); |
| ASSERT_EQ(OK, requestStatus); |
| |
| ASSERT_EQ(static_cast<int32_t>(OK), disconnect(NATIVE_WINDOW_API_CPU, HDisconnectMode::API)); |
| |
| int32_t status = detachBuffer(slot); |
| // According to the HGBP spec, this should return BAD_VALUE: |
| EXPECT_EQ(static_cast<int32_t>(BAD_VALUE), status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Disconnected_QueueBuffer) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| auto [dequeueStatus, slot, fence, timestamps] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| |
| auto [requestStatus, buffer] = requestBuffer(slot); |
| ASSERT_EQ(OK, requestStatus); |
| |
| ASSERT_EQ(static_cast<int32_t>(OK), disconnect(NATIVE_WINDOW_API_CPU, HDisconnectMode::API)); |
| |
| HGraphicBufferProducer::QueueBufferInput qInput{}; |
| auto [status, qOutput] = queueBuffer(slot, qInput); |
| EXPECT_EQ(BAD_VALUE, status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Disconnected_CancelBuffer) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| auto [dequeueStatus, slot, fence, timestamps] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| |
| ASSERT_EQ(static_cast<int32_t>(OK), disconnect(NATIVE_WINDOW_API_CPU, HDisconnectMode::API)); |
| |
| int32_t status = cancelBuffer(slot, ::android::hardware::hidl_handle()); |
| EXPECT_EQ(static_cast<int32_t>(BAD_VALUE), status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Disconnected_SetMaxDequeuedBufferCount) { |
| int32_t status = setMaxDequeuedBufferCount(2); |
| EXPECT_EQ(static_cast<int32_t>(OK), status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Disconnected_SetAsyncMode) { |
| int32_t status = setAsyncMode(true); |
| EXPECT_EQ(static_cast<int32_t>(OK), status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Disconnected_AllocateBuffers) { |
| allocateBuffers(100, 100, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage); |
| // allocateBuffers is void in 1.0 |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Disconnected_AllowAllocation) { |
| int32_t status = allowAllocation(true); |
| EXPECT_EQ(static_cast<int32_t>(OK), status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Disconnected_SetGenerationNumber) { |
| int32_t status = setGenerationNumber(1); |
| EXPECT_EQ(static_cast<int32_t>(OK), status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Disconnected_SetDequeueTimeout) { |
| int32_t status = setDequeueTimeout(1000); |
| EXPECT_EQ(static_cast<int32_t>(OK), status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, GetConsumerName) { |
| mConsumer->setName(String8("name-for-test")); |
| |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| std::string name = getConsumerName(); |
| EXPECT_EQ("name-for-test", name); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Query_Succeeds) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| auto [value, result] = query(NATIVE_WINDOW_WIDTH); |
| EXPECT_EQ(0, result); |
| EXPECT_EQ(1u, static_cast<uint32_t>(value)); // Default width |
| |
| std::tie(value, result) = query(NATIVE_WINDOW_HEIGHT); |
| EXPECT_EQ(0, result); |
| EXPECT_EQ(1u, static_cast<uint32_t>(value)); // Default height |
| |
| std::tie(value, result) = query(NATIVE_WINDOW_FORMAT); |
| EXPECT_EQ(0, result); |
| EXPECT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, value); // Default format |
| |
| std::tie(value, result) = query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS); |
| EXPECT_EQ(0, result); |
| EXPECT_LE(0, value); |
| EXPECT_GE(BufferQueueDefs::NUM_BUFFER_SLOTS, value); |
| |
| std::tie(value, result) = query(NATIVE_WINDOW_CONSUMER_USAGE_BITS); |
| EXPECT_EQ(0, result); |
| EXPECT_EQ(kDefaultUsage, static_cast<uint64_t>(value)); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Query_ReturnsError) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| auto [value, result] = query(-1); // Invalid what |
| EXPECT_EQ(static_cast<int32_t>(BAD_VALUE), result); |
| |
| std::tie(value, result) = query(0xDEADBEEF); // Invalid what |
| EXPECT_EQ(static_cast<int32_t>(BAD_VALUE), result); |
| |
| // NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER is not supported |
| std::tie(value, result) = query(NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER); |
| EXPECT_EQ(0, result); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Queue_ReturnsError) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| HGraphicBufferProducer::QueueBufferInput qInput{}; |
| auto [status, qOutput] = queueBuffer(-1, qInput); // Invalid slot |
| EXPECT_EQ(BAD_VALUE, status); |
| |
| auto [dequeueStatus, slot, fence, timestamps] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| |
| // Queue without request |
| std::tie(status, qOutput) = queueBuffer(slot, qInput); |
| EXPECT_EQ(OK, status); |
| |
| auto [requestStatus, buffer] = requestBuffer(slot); |
| ASSERT_EQ(OK, requestStatus); |
| |
| // Crop rect out of bounds |
| qInput.crop.left = 0; |
| qInput.crop.top = 0; |
| qInput.crop.right = 2; |
| qInput.crop.bottom = 2; |
| std::tie(status, qOutput) = queueBuffer(slot, qInput); |
| EXPECT_EQ(BAD_VALUE, status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, CancelBuffer_Succeeds) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| auto [dequeueStatus, slot, fence, timestamps] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| |
| int32_t status = cancelBuffer(slot, ::android::hardware::hidl_handle()); |
| EXPECT_EQ(static_cast<int32_t>(OK), status); |
| |
| // Second cancel should fail as the buffer is no longer dequeued |
| // But Surface implementation allows it, so we expect OK. |
| status = cancelBuffer(slot, ::android::hardware::hidl_handle()); |
| EXPECT_EQ(static_cast<int32_t>(OK), status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, SetMaxDequeuedBufferCount_Succeeds) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| EXPECT_EQ(static_cast<int32_t>(OK), setMaxDequeuedBufferCount(2)); |
| EXPECT_EQ(static_cast<int32_t>(OK), |
| setMaxDequeuedBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS)); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, SetMaxDequeuedBufferCount_Fails) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| EXPECT_EQ(static_cast<int32_t>(BAD_VALUE), setMaxDequeuedBufferCount(0)); |
| EXPECT_EQ(static_cast<int32_t>(BAD_VALUE), |
| setMaxDequeuedBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS + 1)); |
| |
| // Dequeue 2 buffers |
| ASSERT_EQ(static_cast<int32_t>(OK), setMaxDequeuedBufferCount(2)); |
| for (int i = 0; i < 2; ++i) { |
| auto [dequeueStatus, slot, fence, timestamps] = |
| dequeueBuffer(1, 1, |
| ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| } |
| // Cannot set max lower than the number currently dequeued |
| EXPECT_EQ(static_cast<int32_t>(BAD_VALUE), setMaxDequeuedBufferCount(1)); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, SetAsyncMode_Succeeds) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| EXPECT_EQ(static_cast<int32_t>(OK), setAsyncMode(true)); |
| EXPECT_EQ(static_cast<int32_t>(OK), setAsyncMode(false)); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, DisallowAllocation) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, true); |
| ASSERT_EQ(OK, connectStatus); |
| |
| EXPECT_EQ(static_cast<int32_t>(OK), setDequeueTimeout(0)); |
| |
| EXPECT_EQ(static_cast<int32_t>(OK), allowAllocation(false)); |
| auto [dequeueStatus, slot, fence, timestamps] = |
| dequeueBuffer(100, 100, |
| ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| EXPECT_EQ(TIMED_OUT, dequeueStatus); |
| |
| EXPECT_EQ(static_cast<int32_t>(OK), allowAllocation(true)); |
| std::tie(dequeueStatus, slot, fence, timestamps) = |
| dequeueBuffer(100, 100, |
| ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| EXPECT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, CanAttachWhileDisallowingAllocation) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| // Use NATIVE_WINDOW_API_CPU with producerControlledByApp=true |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, true); |
| ASSERT_EQ(OK, connectStatus); |
| |
| auto [dequeueStatus, slot, fence, timestamps] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| |
| auto [requestStatus, buffer] = requestBuffer(slot); |
| ASSERT_EQ(OK, requestStatus); |
| ASSERT_EQ(static_cast<int32_t>(OK), detachBuffer(slot)); |
| |
| EXPECT_EQ(static_cast<int32_t>(OK), allowAllocation(false)); |
| |
| auto [attachStatus, attachSlot] = attachBuffer(buffer); |
| EXPECT_EQ(OK, attachStatus); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Detach_BufferIsNotLeaked) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| auto [dequeueStatus, slot, fence, timestamps] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| |
| { |
| auto [requestStatus, buffer] = requestBuffer(slot); |
| ASSERT_EQ(OK, requestStatus); |
| } |
| |
| auto [queueStatus, qOutput] = queueBuffer(slot, {}); |
| ASSERT_EQ(OK, queueStatus); |
| |
| wp<GraphicBuffer> weakBuffer; |
| { |
| BufferItem item; |
| ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); |
| weakBuffer = item.mGraphicBuffer; |
| ASSERT_EQ(OK, mConsumer->releaseBuffer(item, item.mFence)); |
| } |
| |
| ASSERT_NE(nullptr, weakBuffer.promote()); |
| |
| auto [dequeueStatus2, slot2, fence2, timestamps2] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus2 == OK || dequeueStatus2 == 1); |
| |
| // Ensure the buffer is the same we got previously |
| // 1.0 doesn't verify bufferNeedsReallocation in output, assumed consistent if implementation |
| // reuses. |
| ASSERT_EQ(slot, slot2); |
| |
| ASSERT_EQ(static_cast<int32_t>(OK), detachBuffer(slot)); |
| ASSERT_EQ(nullptr, weakBuffer.promote()); |
| } |
| |
| // TODO(b/478953869): Disabled until the underlying surface bug is fixed. |
| TEST_F(Surface2HGraphicBufferProducer10Test, DISABLED_ConsumerDetach_BufferIsNotLeaked) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| auto [dequeueStatus, slot, fence, timestamps] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| |
| { |
| auto [requestStatus, buffer] = requestBuffer(slot); |
| ASSERT_EQ(OK, requestStatus); |
| } |
| |
| HGraphicBufferProducer::QueueBufferInput qbi; |
| auto [status, qOutput] = queueBuffer(slot, qbi); |
| ASSERT_EQ(OK, status); |
| |
| wp<GraphicBuffer> weakBuffer; |
| { |
| BufferItem item; |
| ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); |
| weakBuffer = item.mGraphicBuffer; |
| ASSERT_EQ(OK, mConsumer->detachBuffer(item.mGraphicBuffer)); |
| ASSERT_NE(nullptr, weakBuffer.promote()); |
| } |
| |
| EXPECT_EQ(nullptr, weakBuffer.promote()); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Dequeue_InvalidDimensions_ReturnsError) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| auto [status, slot, fence, timestamps] = |
| dequeueBuffer(1, 0, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| EXPECT_EQ(BAD_VALUE, status); |
| |
| std::tie(status, slot, fence, timestamps) = |
| dequeueBuffer(0, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| EXPECT_EQ(BAD_VALUE, status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Attach_BadGenerationNumber_ReturnsError) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| // Set generation number to 10 |
| EXPECT_EQ(static_cast<int32_t>(OK), setGenerationNumber(10)); |
| |
| // Dequeue a buffer (will have generation 10) |
| auto [dequeueStatus, slot, fence, timestamps] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| auto [requestStatus, buffer] = requestBuffer(slot); |
| ASSERT_EQ(OK, requestStatus); |
| |
| ASSERT_EQ(static_cast<int32_t>(OK), detachBuffer(slot)); |
| |
| // Try to attach with wrong generation number (e.g., 5) |
| // In 1.0, attachBuffer doesn't take generationNumber as a separate argument, |
| // it's embedded in AnwBuffer. But if we didn't update it, it should still have 10. |
| // However, the test intent is to see it failing if it doesn't match. |
| // For this test we'd need a way to modify generation in AnwBuffer or just rely on the fact |
| // that if it doesn't match mBase's generation it fails. |
| |
| // Actually, setGenerationNumber(11) then attaching the buffer with gen 10 should fail. |
| EXPECT_EQ(static_cast<int32_t>(OK), setGenerationNumber(11)); |
| |
| auto [attachStatus, attachSlot] = attachBuffer(buffer); |
| EXPECT_EQ(BAD_VALUE, attachStatus); |
| |
| // Try to attach with correct generation number (11) |
| EXPECT_EQ(static_cast<int32_t>(OK), setGenerationNumber(10)); |
| std::tie(attachStatus, attachSlot) = attachBuffer(buffer); |
| EXPECT_EQ(OK, attachStatus); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Attach_TooManyDequeued_ReturnsError) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| // First queue and dequeue, because BQs allow for unlimited dequeued right after a connect, then |
| // enforce the limit. |
| { |
| auto [dequeueStatus1, slot1, fence1, timestamps1] = |
| dequeueBuffer(1, 1, |
| ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus1 == OK || dequeueStatus1 == 1); |
| auto [queueStatus1, qOutput1] = queueBuffer(slot1, {}); |
| ASSERT_EQ(OK, queueStatus1); |
| } |
| |
| // Set the max dequeued buffer count to 2 so we can dequeue 2 buffers and detach one of them. |
| ASSERT_EQ(static_cast<int32_t>(OK), setMaxDequeuedBufferCount(2)); |
| |
| auto [dequeueStatus1, slot1, fence1, timestamps1] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus1 == OK || dequeueStatus1 == 1); |
| |
| auto [dequeueStatus2, slot2, fence2, timestamps2] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus2 == OK || dequeueStatus2 == 1); |
| auto [requestStatus, testBuffer] = requestBuffer(slot2); |
| ASSERT_EQ(OK, requestStatus); |
| ASSERT_EQ(static_cast<int32_t>(OK), detachBuffer(slot2)); |
| |
| // Now set max to 1. Slot1 is still dequeued. |
| ASSERT_EQ(static_cast<int32_t>(OK), setMaxDequeuedBufferCount(1)); |
| |
| auto [attachStatus, attachSlot] = attachBuffer(testBuffer); |
| EXPECT_EQ(INVALID_OPERATION, attachStatus); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, RequestBuffer_InvalidSlot_ReturnsBadValue) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| // Request buffer with an invalid slot number |
| auto [status, buffer] = requestBuffer(-1); |
| EXPECT_EQ(BAD_VALUE, status); |
| |
| // Request buffer with a slot that has not been dequeued |
| std::tie(status, buffer) = requestBuffer(0); |
| EXPECT_EQ(BAD_VALUE, status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, |
| DequeueBuffer_TooManyDequeued_ReturnsInvalidOperation) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| // Dequeue and queue a buffer to prime the buffer queue |
| auto [dequeueStatus, slot, fence, timestamps] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| auto [queueStatus, qOutput] = queueBuffer(slot, {}); |
| ASSERT_EQ(OK, queueStatus); |
| |
| // Set max dequeued buffer count to 1 |
| ASSERT_EQ(static_cast<int32_t>(OK), setMaxDequeuedBufferCount(1)); |
| |
| // Dequeue one buffer, which should succeed |
| std::tie(dequeueStatus, slot, fence, timestamps) = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| |
| // Try to dequeue another buffer, which should fail |
| auto [dequeueStatus2, slot2, fence2, timestamps2] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| EXPECT_EQ(INVALID_OPERATION, dequeueStatus2); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, DequeueBuffer_WouldBlock) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| // Connect in a mode where producer and consumer are controlled by the app |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, true); |
| ASSERT_EQ(OK, connectStatus); |
| |
| // Dequeue and queue a buffer to prime the buffer queue |
| auto [dequeueStatus, slot, fence, timestamps] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| auto [queueStatus, qOutput] = queueBuffer(slot, {}); |
| ASSERT_EQ(OK, queueStatus); |
| |
| // Set max dequeued buffer count to 1 and dequeue a buffer |
| ASSERT_EQ(static_cast<int32_t>(OK), setMaxDequeuedBufferCount(1)); |
| std::tie(dequeueStatus, slot, fence, timestamps) = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| ASSERT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| |
| // Dequeueing another buffer should return WOULD_BLOCK (mapped to INVALID_OPERATION in this case |
| // by Surface) because this is a non-blocking call and no buffers are available. |
| auto [dequeueStatus2, slot2, fence2, timestamps2] = |
| dequeueBuffer(1, 1, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| EXPECT_EQ(INVALID_OPERATION, dequeueStatus2); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, Connect_NoConsumer_ReturnsNoInit) { |
| sp<Surface> surface; |
| sp<BufferItemConsumer> consumer; |
| std::tie(consumer, surface) = BufferItemConsumer::create(kDefaultUsage); |
| consumer->abandon(); |
| |
| sp<Surface2HGraphicBufferProducer> hgbp = sp<Surface2HGraphicBufferProducer>::make(surface); |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| int status; |
| hgbp->connect(listener, NATIVE_WINDOW_API_CPU, false, |
| [&](int s, const HGraphicBufferProducer::QueueBufferOutput&) { status = s; }); |
| EXPECT_EQ(NO_INIT, status); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, AllocateBuffers_ThenDequeue_Succeeds) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, true); |
| ASSERT_EQ(OK, connectStatus); |
| |
| EXPECT_EQ(static_cast<int32_t>(OK), setDequeueTimeout(0)); |
| |
| // Allocate buffers |
| allocateBuffers(100, 100, ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage); |
| |
| // Disallow allocation and dequeue, which should succeed |
| EXPECT_EQ(static_cast<int32_t>(OK), allowAllocation(false)); |
| auto [dequeueStatus, slot, fence, timestamps] = |
| dequeueBuffer(100, 100, |
| ::android::hardware::graphics::common::V1_0::PixelFormat::RGBA_8888, |
| kDefaultUsage, false); |
| EXPECT_TRUE(dequeueStatus == OK || dequeueStatus == 1); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, GetUniqueId_IsUnique) { |
| uint64_t id1 = getUniqueId(); |
| EXPECT_NE(0u, id1); |
| |
| // Create a second BufferQueue and get its ID |
| sp<Surface> surface; |
| sp<BufferItemConsumer> consumer; |
| std::tie(consumer, surface) = BufferItemConsumer::create(kDefaultUsage); |
| sp<Surface2HGraphicBufferProducer> hgbp2 = sp<Surface2HGraphicBufferProducer>::make(surface); |
| uint64_t id2 = 0; |
| hgbp2->getUniqueId([&](int32_t, uint64_t id) { id2 = id; }); |
| EXPECT_NE(0u, id2); |
| |
| EXPECT_NE(id1, id2); |
| } |
| |
| TEST_F(Surface2HGraphicBufferProducer10Test, DetachNextBuffer_NoFreeBuffer_ReturnsNoMemory) { |
| sp<TestHProducerListener> listener = sp<TestHProducerListener>::make(); |
| auto [connectStatus, connectOutput] = connect(listener, NATIVE_WINDOW_API_CPU, false); |
| ASSERT_EQ(OK, connectStatus); |
| |
| // No buffers are available to be detached, so this should fail. |
| auto [status, buffer, fence] = detachNextBuffer(); |
| EXPECT_EQ(NO_MEMORY, status); |
| } |
| |
| } // namespace android |