blob: f562d3177b5756bfce882c35a585ef0fde918557 [file]
/*
* 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