blob: 7d6ed3eb995977739ca0fd8948b188f04baf1813 [file]
/*
* Copyright 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_NDEBUG 0
#define LOG_TAG "Surface2HGraphicBufferProducer@1.0"
#include <android-base/logging.h>
#include <utils/Log.h>
#include <android/hardware/graphics/common/1.0/types.h>
#include <gui/BufferQueue.h>
#include <gui/Surface.h>
#include <gui/bufferqueue/1.0/Conversion.h>
#include <gui/bufferqueue/1.0/H2BProducerListener.h>
#include <ui/BufferQueueDefs.h>
#include <ui/GraphicBuffer.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <vndk/hardware_buffer.h>
#include <gui/bufferqueue/1.0/Surface2HGraphicBufferProducer.h>
namespace android {
namespace hardware {
namespace graphics {
namespace bufferqueue {
namespace V1_0 {
namespace utils {
namespace /* unnamed */ {
using BQueueBufferInput = ::android::IGraphicBufferProducer::QueueBufferInput;
using HQueueBufferInput =
::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer::QueueBufferInput;
using BQueueBufferOutput = ::android::IGraphicBufferProducer::QueueBufferOutput;
using HQueueBufferOutput =
::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer::QueueBufferOutput;
} // unnamed namespace
class Surface2HGraphicBufferProducer::HidlBridgeListener final : public SurfaceListener {
public:
HidlBridgeListener(const sp<Surface2HGraphicBufferProducer>& producer,
const sp<HProducerListener>& listener)
: mProducer(producer), mListener(listener) {}
virtual bool needsReleaseNotify() override { return true; }
virtual void onBufferReleased() override { mListener->onBufferReleased(); }
virtual void onBufferDetached(uint64_t /*bufferId*/) override {}
virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers) override {
if (auto producer = mProducer.promote()) {
producer->onBuffersDiscarded(buffers);
}
}
virtual void onRemoteDied() override {
if (auto producer = mProducer.promote()) {
producer->onRemoteDied();
}
}
virtual bool needsDeathNotify() override { return true; }
private:
wp<Surface2HGraphicBufferProducer> mProducer;
const sp<HProducerListener> mListener;
};
struct Surface2HGraphicBufferProducer::Obituary : public hardware::hidl_death_recipient {
wp<Surface2HGraphicBufferProducer> producer;
sp<HProducerListener> listener;
int32_t apiType;
Obituary(const wp<Surface2HGraphicBufferProducer>& p, const sp<HProducerListener>& l, int32_t t)
: producer(p), listener(l), apiType(t) {}
void serviceDied(uint64_t /* cookie */,
const wp<::android::hidl::base::V1_0::IBase>& /* who */) override {
sp<Surface2HGraphicBufferProducer> dr = producer.promote();
if (dr != nullptr) {
(void)dr->disconnect(apiType, HGraphicBufferProducer::DisconnectMode::API);
}
}
};
Surface2HGraphicBufferProducer::Surface2HGraphicBufferProducer(
const sp<::android::Surface>& surface)
: mBase(surface) {
resetSlotsLocked();
}
::android::hardware::Return<void> Surface2HGraphicBufferProducer::requestBuffer(
int32_t slot, requestBuffer_cb _hidl_cb) {
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
ALOGE("Surface2HGraphicBufferProducer::requestBuffer Invalid slot: %d", slot);
_hidl_cb(static_cast<int32_t>(BAD_VALUE), AnwBuffer{});
return {};
}
std::scoped_lock _l(mMutex);
sp<GraphicBuffer> buffer = mSlotToInfo[slot].buffer;
if (buffer == nullptr) {
ALOGE("Surface2HGraphicBufferProducer::requestBuffer Invalid slot: %d", slot);
_hidl_cb(static_cast<int32_t>(BAD_VALUE), AnwBuffer{});
return {};
}
AnwBuffer hBuffer{};
::android::conversion::wrapAs(&hBuffer, *buffer);
_hidl_cb(static_cast<int32_t>(OK), hBuffer);
return {};
}
::android::hardware::Return<int32_t> Surface2HGraphicBufferProducer::setMaxDequeuedBufferCount(
int32_t maxDequeuedBuffers) {
// Since we only provide NUM_BUFFER_SLOTS buffers, we can't allow more than that.
if (maxDequeuedBuffers < 0 || maxDequeuedBuffers > BufferQueueDefs::NUM_BUFFER_SLOTS) {
ALOGE("Surface2HGraphicBufferProducer::setMaxDequeuedBufferCount Invalid "
"maxDequeuedBuffers: %d",
maxDequeuedBuffers);
return {static_cast<int32_t>(BAD_VALUE)};
}
return {mBase->setMaxDequeuedBufferCount(static_cast<int>(maxDequeuedBuffers))};
}
::android::hardware::Return<int32_t> Surface2HGraphicBufferProducer::setAsyncMode(bool async) {
return {mBase->setAsyncMode(async)};
}
::android::hardware::Return<void> Surface2HGraphicBufferProducer::dequeueBuffer(
uint32_t width, uint32_t height,
::android::hardware::graphics::common::V1_0::PixelFormat format, uint32_t usage,
bool getFrameTimestamps, dequeueBuffer_cb _hidl_cb) {
(void)getFrameTimestamps;
std::scoped_lock _l(mMutex);
status_t status = NO_ERROR;
status = mBase->setBuffersDimensions(width, height);
if (status != NO_ERROR) {
ALOGE("Surface2HGraphicBufferProducer::dequeueBuffer setBuffersDimensions failed: %d",
status);
_hidl_cb(static_cast<int32_t>(status), -1, hidl_handle{}, {});
return {};
}
status = mBase->setBuffersFormat(static_cast<android::PixelFormat>(format));
if (status != NO_ERROR) {
ALOGE("Surface2HGraphicBufferProducer::dequeueBuffer setBuffersFormat failed: %d", status);
_hidl_cb(static_cast<int32_t>(status), -1, hidl_handle{}, {});
return {};
}
status = mBase->setUsage(usage);
if (status != NO_ERROR) {
ALOGE("Surface2HGraphicBufferProducer::dequeueBuffer setUsage failed: %d", status);
_hidl_cb(static_cast<int32_t>(status), -1, hidl_handle{}, {});
return {};
}
sp<GraphicBuffer> buffer;
sp<Fence> fence;
status = mBase->dequeueBuffer(&buffer, &fence);
if (status < NO_ERROR) {
if (status != WOULD_BLOCK) {
ALOGE("Surface2HGraphicBufferProducer::dequeueBuffer dequeue failed: %d", status);
}
_hidl_cb(static_cast<int32_t>(status), -1, hidl_handle{}, {});
return {};
}
int slot = getSlotForBufferLocked(buffer);
if (slot == BufferQueue::INVALID_BUFFER_SLOT) {
slot = getFreeOrEvictableSlotLocked();
if (slot == BufferQueue::INVALID_BUFFER_SLOT) {
ALOGE("SBHGBP::dequeueBuffer No available slots for buffer %" PRIu64, buffer->getId());
status_t ret = mBase->cancelBuffer(buffer, fence);
if (ret != NO_ERROR) {
ALOGE("SBHGBP::dequeueBuffer cancelBuffer failed: %d, we're in a potentially bad "
"state",
ret);
}
_hidl_cb(static_cast<int32_t>(NO_MEMORY), -1, hidl_handle{}, {});
return {};
}
status |= android::IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION;
mSlotToInfo[slot] = {.needsRefresh = true, .isDequeued = true, .buffer = buffer};
} else {
if (mSlotToInfo[slot].needsRefresh) {
status |= android::IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION;
}
mSlotToInfo[slot].isDequeued = true;
mSlotToInfo[slot].needsRefresh = false;
}
hidl_handle hFence;
native_handle_t* nh = nullptr;
if (fence != nullptr) {
::android::conversion::wrapAs(&hFence, &nh, *fence);
}
// TODO: support frame timestamps if needed
_hidl_cb(static_cast<int32_t>(status), slot, hFence, {});
if (nh != nullptr) {
native_handle_delete(nh);
}
return {};
}
::android::hardware::Return<int32_t> Surface2HGraphicBufferProducer::detachBuffer(int32_t slot) {
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
ALOGE("Surface2HGraphicBufferProducer::detachBuffer Invalid slot: %d", slot);
return {static_cast<int32_t>(BAD_VALUE)};
}
std::scoped_lock _l(mMutex);
sp<GraphicBuffer> buffer = mSlotToInfo[slot].buffer;
if (!buffer) {
ALOGE("Surface2HGraphicBufferProducer::detachBuffer Invalid slot: %d", slot);
return {static_cast<int32_t>(BAD_VALUE)};
}
status_t status = mBase->detachBuffer(buffer);
mSlotToInfo[slot].isDequeued = false;
clearSlotLocked(slot);
return {static_cast<int32_t>(status)};
}
::android::hardware::Return<void> Surface2HGraphicBufferProducer::detachNextBuffer(
detachNextBuffer_cb _hidl_cb) {
sp<GraphicBuffer> bBuffer;
sp<Fence> bFence;
std::scoped_lock _l(mMutex);
status_t status = mBase->detachNextBuffer(&bBuffer, &bFence);
if (status != NO_ERROR) {
_hidl_cb(static_cast<int32_t>(status), AnwBuffer{}, hidl_handle{});
return {};
}
AnwBuffer hBuffer{};
::android::conversion::wrapAs(&hBuffer, *bBuffer);
hidl_handle hFence;
native_handle_t* nh = nullptr;
if (bFence != nullptr) {
::android::conversion::wrapAs(&hFence, &nh, *bFence);
}
_hidl_cb(static_cast<int32_t>(status), hBuffer, hFence);
if (status == OK) {
int slot = getSlotForBufferLocked(bBuffer);
if (slot != BufferQueue::INVALID_BUFFER_SLOT) {
clearSlotLocked(slot);
}
}
if (nh != nullptr) {
native_handle_delete(nh);
}
return {};
}
::android::hardware::Return<void> Surface2HGraphicBufferProducer::attachBuffer(
const ::android::hardware::media::V1_0::AnwBuffer& buffer, attachBuffer_cb _hidl_cb) {
std::scoped_lock _l(mMutex);
int slot = getFreeOrEvictableSlotLocked();
if (slot == BufferQueue::INVALID_BUFFER_SLOT) {
_hidl_cb(static_cast<int32_t>(NO_MEMORY), -1);
return {};
}
sp<GraphicBuffer> bBuffer = sp<GraphicBuffer>::make();
if (!::android::conversion::convertTo(bBuffer.get(), buffer) || !bBuffer) {
_hidl_cb(static_cast<int32_t>(UNKNOWN_ERROR), -1);
return {};
}
// HGBPs are required to fail if the buffer's generation number doesn't match that of the HGBP.
mBase->setAutoGenerationUpdate(false);
status_t ret = mBase->attachBuffer(bBuffer);
if (ret == OK) {
mSlotToInfo[slot] = {.needsRefresh = false, .isDequeued = true, .buffer = bBuffer};
}
_hidl_cb(static_cast<int32_t>(ret), slot);
return {};
}
::android::hardware::Return<void> Surface2HGraphicBufferProducer::queueBuffer(
int32_t slot,
const ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer::
QueueBufferInput& input,
queueBuffer_cb _hidl_cb) {
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
_hidl_cb(static_cast<int32_t>(BAD_VALUE), {});
return {};
}
SurfaceQueueBufferInput sInput{
.dataSpace = static_cast<android_dataspace>(input.dataSpace),
.crop = Rect(input.crop.left, input.crop.top, input.crop.right, input.crop.bottom),
.scalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE,
.transform = static_cast<uint32_t>(input.transform),
.stickyTransform = static_cast<uint32_t>(input.stickyTransform),
.timestamp = input.timestamp,
.isAutoTimestamp = input.isAutoTimestamp,
.getFrameTimestamps = input.getFrameTimestamps,
};
sp<Fence> bFence = sp<Fence>::make();
if (!::android::conversion::convertTo(bFence.get(), input.fence)) {
_hidl_cb(static_cast<int32_t>(UNKNOWN_ERROR), {});
return {};
}
sInput.fence = bFence;
if (input.surfaceDamage.size() > 0) {
if (!::android::conversion::convertTo(&sInput.surfaceDamage, input.surfaceDamage)) {
_hidl_cb(static_cast<int32_t>(UNKNOWN_ERROR), {});
return {};
}
}
std::scoped_lock _l(mMutex);
sp<GraphicBuffer> buffer = mSlotToInfo[slot].buffer;
if (!buffer) {
_hidl_cb(static_cast<int32_t>(BAD_VALUE), {});
return {};
}
SurfaceQueueBufferOutput bOutput{};
status_t result = mBase->queueBuffer(buffer, sInput, &bOutput);
if (result == NO_ERROR) {
mSlotToInfo[slot].isDequeued = false;
}
HQueueBufferOutput hOutput{};
BQueueBufferOutput bqOutput{};
bqOutput.width = bOutput.width;
bqOutput.height = bOutput.height;
bqOutput.transformHint = bOutput.transformHint;
bqOutput.numPendingBuffers = bOutput.numPendingBuffers;
bqOutput.nextFrameNumber = bOutput.nextFrameNumber;
bqOutput.bufferReplaced = bOutput.bufferReplaced;
std::vector<std::vector<native_handle_t*>> nh;
::android::conversion::wrapAs(&hOutput, &nh, bqOutput);
_hidl_cb(static_cast<int32_t>(result), hOutput);
for (auto& v : nh) {
for (auto h : v) {
if (h != nullptr) {
native_handle_delete(h);
}
}
}
return {};
}
::android::hardware::Return<int32_t> Surface2HGraphicBufferProducer::cancelBuffer(
int32_t slot, const ::android::hardware::hidl_handle& fence) {
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
ALOGE("Surface2HGraphicBufferProducer::cancelBuffer Invalid slot: %d", slot);
return {static_cast<int32_t>(BAD_VALUE)};
}
std::scoped_lock _l(mMutex);
sp<GraphicBuffer> bBuffer = mSlotToInfo[slot].buffer;
if (!bBuffer) {
ALOGE("Surface2HGraphicBufferProducer::cancelBuffer Invalid slot: %d", slot);
return {static_cast<int32_t>(BAD_VALUE)};
}
sp<Fence> bFence = sp<Fence>::make();
if (!::android::conversion::convertTo(bFence.get(), fence)) {
return {static_cast<int32_t>(UNKNOWN_ERROR)};
}
status_t status = mBase->cancelBuffer(bBuffer, bFence);
if (status == NO_ERROR) {
mSlotToInfo[slot].isDequeued = false;
}
return {static_cast<int32_t>(status)};
}
::android::hardware::Return<void> Surface2HGraphicBufferProducer::query(int32_t what,
query_cb _hidl_cb) {
int value{};
int result = mBase->query(static_cast<int>(what), &value);
_hidl_cb(static_cast<int32_t>(result), static_cast<int32_t>(value));
return {};
}
::android::hardware::Return<void> Surface2HGraphicBufferProducer::connect(
const ::android::sp<::android::hardware::graphics::bufferqueue::V1_0::IProducerListener>&
listener,
int32_t api, bool producerControlledByApp, connect_cb _hidl_cb) {
std::scoped_lock _l(mMutex);
sp<SurfaceListener> bListener =
sp<HidlBridgeListener>::make(sp<Surface2HGraphicBufferProducer>::fromExisting(this),
listener);
if (!bListener) {
_hidl_cb(static_cast<int32_t>(UNKNOWN_ERROR), {});
return {};
}
mBase->setProducerControlledByApp(producerControlledByApp);
status_t result = mBase->connect(api, bListener, true);
if (result == OK) {
mObituary = sp<Obituary>::make(sp<Surface2HGraphicBufferProducer>::fromExisting(this),
listener, api);
if (listener != nullptr) {
listener->linkToDeath(mObituary, 0);
}
}
resetSlotsLocked();
BQueueBufferOutput bOutput{};
mBase->query(NATIVE_WINDOW_WIDTH, reinterpret_cast<int*>(&bOutput.width));
mBase->query(NATIVE_WINDOW_HEIGHT, reinterpret_cast<int*>(&bOutput.height));
int transformHint = 0;
mBase->query(NATIVE_WINDOW_TRANSFORM_HINT, &transformHint);
bOutput.transformHint = static_cast<uint32_t>(transformHint);
bOutput.nextFrameNumber = mBase->getNextFrameNumber();
HQueueBufferOutput hOutput{};
std::vector<std::vector<native_handle_t*>> nh;
::android::conversion::wrapAs(&hOutput, &nh, bOutput);
_hidl_cb(static_cast<int32_t>(result), hOutput);
for (auto& v : nh) {
for (auto h : v) {
if (h != nullptr) {
native_handle_delete(h);
}
}
}
return {};
}
::android::hardware::Return<int32_t> Surface2HGraphicBufferProducer::disconnect(
int32_t api,
::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer::DisconnectMode
mode) {
std::scoped_lock _l(mMutex);
status_t result = mBase->disconnect(api, ::android::conversion::toGuiDisconnectMode(mode));
resetSlotsLocked();
if (mObituary != nullptr) {
if (mObituary->listener != nullptr) {
mObituary->listener->unlinkToDeath(mObituary);
}
mObituary.clear();
}
return {static_cast<int32_t>(result)};
}
::android::hardware::Return<int32_t> Surface2HGraphicBufferProducer::setSidebandStream(
const ::android::hardware::hidl_handle& stream) {
mBase->setSidebandStream(
NativeHandle::create(stream.getNativeHandle()
? native_handle_clone(stream.getNativeHandle())
: nullptr,
true));
return {static_cast<int32_t>(OK)};
}
::android::hardware::Return<void> Surface2HGraphicBufferProducer::allocateBuffers(
uint32_t width, uint32_t height,
::android::hardware::graphics::common::V1_0::PixelFormat format, uint32_t usage) {
std::scoped_lock _l(mMutex);
mBase->setBuffersDimensions(width, height);
mBase->setBuffersFormat(static_cast<android::PixelFormat>(format));
mBase->setUsage(usage);
mBase->allocateBuffers();
return {};
}
::android::hardware::Return<int32_t> Surface2HGraphicBufferProducer::allowAllocation(bool allow) {
return {mBase->allowAllocation(allow)};
}
::android::hardware::Return<int32_t> Surface2HGraphicBufferProducer::setGenerationNumber(
uint32_t generationNumber) {
return {mBase->setGenerationNumber(generationNumber)};
}
::android::hardware::Return<void> Surface2HGraphicBufferProducer::getConsumerName(
getConsumerName_cb _hidl_cb) {
_hidl_cb(hidl_string{mBase->getConsumerName().c_str()});
return {};
}
::android::hardware::Return<int32_t> Surface2HGraphicBufferProducer::setSharedBufferMode(
bool sharedBufferMode) {
return {mBase->setSharedBufferMode(sharedBufferMode)};
}
::android::hardware::Return<int32_t> Surface2HGraphicBufferProducer::setAutoRefresh(
bool autoRefresh) {
return {mBase->setAutoRefresh(autoRefresh)};
}
::android::hardware::Return<int32_t> Surface2HGraphicBufferProducer::setDequeueTimeout(
int64_t timeoutNs) {
return {mBase->setDequeueTimeout(static_cast<nsecs_t>(timeoutNs))};
}
::android::hardware::Return<void> Surface2HGraphicBufferProducer::getLastQueuedBuffer(
getLastQueuedBuffer_cb _hidl_cb) {
sp<GraphicBuffer> buffer;
sp<Fence> fence;
float matrix[16];
status_t result = mBase->getLastQueuedBuffer(&buffer, &fence, matrix);
if (result != NO_ERROR) {
_hidl_cb(static_cast<int32_t>(result), AnwBuffer{}, hidl_handle{}, hidl_array<float, 16>{});
return {};
}
AnwBuffer hBuffer{};
if (buffer != nullptr) {
::android::conversion::wrapAs(&hBuffer, *buffer);
}
hidl_handle hFence;
native_handle_t* nh = nullptr;
if (fence != nullptr) {
::android::conversion::wrapAs(&hFence, &nh, *fence);
}
hidl_array<float, 16> hMatrix;
std::copy(matrix, matrix + 16, hMatrix.data());
_hidl_cb(static_cast<int32_t>(result), hBuffer, hFence, hMatrix);
if (nh != nullptr) {
native_handle_delete(nh);
}
return {};
}
::android::hardware::Return<void> Surface2HGraphicBufferProducer::getFrameTimestamps(
getFrameTimestamps_cb _hidl_cb) {
::android::FrameEventHistoryDelta bDelta;
mBase->getFrameEventHistoryDelta(&bDelta);
HGraphicBufferProducer::FrameEventHistoryDelta hDelta;
std::vector<std::vector<native_handle_t*>> nh;
::android::conversion::wrapAs(&hDelta, &nh, bDelta);
_hidl_cb(hDelta);
for (auto& v : nh) {
for (auto h : v) {
if (h != nullptr) {
native_handle_delete(h);
}
}
}
return {};
}
::android::hardware::Return<void> Surface2HGraphicBufferProducer::getUniqueId(
getUniqueId_cb _hidl_cb) {
uint64_t outId = 0;
status_t result = mBase->getUniqueId(&outId);
_hidl_cb(static_cast<int32_t>(result), outId);
return {};
}
void Surface2HGraphicBufferProducer::onBuffersDiscarded(
const std::vector<sp<GraphicBuffer>>& buffers) {
std::scoped_lock _l(mMutex);
for (const auto& buffer : buffers) {
int slot = getSlotForBufferLocked(buffer);
if (slot != BufferQueue::INVALID_BUFFER_SLOT) {
clearSlotLocked(slot);
}
}
}
void Surface2HGraphicBufferProducer::onRemoteDied() {
std::scoped_lock _l(mMutex);
ALOGE("Surface2HGraphicBufferProducer::onRemoteDie [%s] called. The underlying surface's "
"remote "
"process has died.",
mConsumerName.c_str());
}
int Surface2HGraphicBufferProducer::getSlotForBufferLocked(const sp<GraphicBuffer>& buffer) {
for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
if (mSlotToInfo[i].buffer == buffer) {
return i;
}
}
return BufferQueue::INVALID_BUFFER_SLOT;
}
int Surface2HGraphicBufferProducer::getFreeOrEvictableSlotLocked() {
// First, if we have any free slots, use one of them.
for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
if (!mSlotToInfo[i].buffer) {
return i;
}
}
// But if we don't, replace a previously dequeued slot.
for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
if (mSlotToInfo[i].buffer && !mSlotToInfo[i].isDequeued) {
return i;
}
}
return BufferQueue::INVALID_BUFFER_SLOT;
}
void Surface2HGraphicBufferProducer::resetSlotsLocked() {
for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
mSlotToInfo[i] = {.needsRefresh = false, .isDequeued = false, .buffer = nullptr};
}
}
void Surface2HGraphicBufferProducer::clearSlotLocked(int slot) {
if (mSlotToInfo[slot].buffer == nullptr || mSlotToInfo[slot].isDequeued) {
return;
}
mSlotToInfo[slot] = {.needsRefresh = false, .isDequeued = false, .buffer = nullptr};
}
} // namespace utils
} // namespace V1_0
} // namespace bufferqueue
} // namespace graphics
} // namespace hardware
} // namespace android