|  | /* | 
|  | * Copyright 2019 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 "H2BGraphicBufferProducer@2.0" | 
|  |  | 
|  | #include <android-base/logging.h> | 
|  |  | 
|  | #include <android/hardware/graphics/common/1.2/types.h> | 
|  | #include <gui/bufferqueue/2.0/B2HProducerListener.h> | 
|  | #include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h> | 
|  | #include <gui/bufferqueue/2.0/types.h> | 
|  | #include <ui/GraphicBuffer.h> | 
|  | #include <ui/Rect.h> | 
|  | #include <ui/Region.h> | 
|  | #include <vndk/hardware_buffer.h> | 
|  |  | 
|  | namespace android { | 
|  | namespace hardware { | 
|  | namespace graphics { | 
|  | namespace bufferqueue { | 
|  | namespace V2_0 { | 
|  | namespace utils { | 
|  |  | 
|  | namespace /* unnamed */ { | 
|  |  | 
|  | using BQueueBufferInput = ::android:: | 
|  | IGraphicBufferProducer::QueueBufferInput; | 
|  | using HQueueBufferInput = ::android::hardware::graphics::bufferqueue::V2_0:: | 
|  | IGraphicBufferProducer::QueueBufferInput; | 
|  | using BQueueBufferOutput = ::android:: | 
|  | IGraphicBufferProducer::QueueBufferOutput; | 
|  | using HQueueBufferOutput = ::android::hardware::graphics::bufferqueue::V2_0:: | 
|  | IGraphicBufferProducer::QueueBufferOutput; | 
|  |  | 
|  | using ::android::hardware::graphics::bufferqueue::V2_0::utils::b2h; | 
|  | using ::android::hardware::graphics::bufferqueue::V2_0::utils::h2b; | 
|  |  | 
|  | bool b2h(BQueueBufferInput const& from, HQueueBufferInput* to, | 
|  | HFenceWrapper* hFenceWrapper) { | 
|  | to->timestamp = from.timestamp; | 
|  | to->isAutoTimestamp = static_cast<bool>(from.isAutoTimestamp); | 
|  | to->dataSpace = static_cast<int32_t>(from.dataSpace); | 
|  | to->transform = static_cast<int32_t>(from.transform); | 
|  | to->stickyTransform = static_cast<int32_t>(from.stickyTransform); | 
|  | if (!b2h(from.crop, &to->crop) || | 
|  | !b2h(from.surfaceDamage, &to->surfaceDamage) || | 
|  | !b2h(from.fence, hFenceWrapper)) { | 
|  | return false; | 
|  | } | 
|  | to->fence = hFenceWrapper->getHandle(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool h2b(HQueueBufferOutput const& from, BQueueBufferOutput* to) { | 
|  | to->width = from.width; | 
|  | to->height = from.height; | 
|  | to->transformHint = static_cast<uint32_t>(from.transformHint); | 
|  | to->numPendingBuffers = from.numPendingBuffers; | 
|  | to->nextFrameNumber = from.nextFrameNumber; | 
|  | to->bufferReplaced = from.bufferReplaced; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | } // unnamed namespace | 
|  |  | 
|  | // H2BGraphicBufferProducer | 
|  | // ======================== | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::requestBuffer(int slot, | 
|  | sp<GraphicBuffer>* bBuffer) { | 
|  | bool converted{}; | 
|  | status_t bStatus{}; | 
|  | Return<void> transResult = mBase->requestBuffer(slot, | 
|  | [&converted, &bStatus, bBuffer]( | 
|  | HStatus hStatus, | 
|  | HardwareBuffer const& hBuffer, | 
|  | uint32_t generationNumber) { | 
|  | converted = | 
|  | h2b(hStatus, &bStatus) && | 
|  | h2b(hBuffer, bBuffer); | 
|  | if (*bBuffer) { | 
|  | (*bBuffer)->setGenerationNumber(generationNumber); | 
|  | } | 
|  | }); | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "requestBuffer: transaction failed."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | if (!converted) { | 
|  | LOG(ERROR) << "requestBuffer: corrupted transaction."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | return bStatus; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::setMaxDequeuedBufferCount( | 
|  | int maxDequeuedBuffers) { | 
|  | status_t bStatus{}; | 
|  | Return<HStatus> transResult = mBase->setMaxDequeuedBufferCount( | 
|  | static_cast<int32_t>(maxDequeuedBuffers)); | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "setMaxDequeuedBufferCount: transaction failed."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { | 
|  | LOG(ERROR) << "setMaxDequeuedBufferCount: corrupted transaction."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | return bStatus; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::setAsyncMode(bool async) { | 
|  | status_t bStatus{}; | 
|  | Return<HStatus> transResult = mBase->setAsyncMode(async); | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "setAsyncMode: transaction failed."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { | 
|  | LOG(ERROR) << "setAsyncMode: corrupted transaction."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | return bStatus; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::dequeueBuffer( | 
|  | int* slot, sp<BFence>* fence, | 
|  | uint32_t w, uint32_t h, | 
|  | PixelFormat format, uint64_t usage, | 
|  | uint64_t* outBufferAge, FrameEventHistoryDelta* /* outTimestamps */) { | 
|  |  | 
|  | using HInput = HGraphicBufferProducer::DequeueBufferInput; | 
|  | HInput input{w, h, static_cast<uint32_t>(format), usage}; | 
|  |  | 
|  | using HOutput = HGraphicBufferProducer::DequeueBufferOutput; | 
|  | bool converted{}; | 
|  | status_t bStatus{}; | 
|  | Return<void> transResult = mBase->dequeueBuffer(input, | 
|  | [&converted, &bStatus, slot, fence, outBufferAge] ( | 
|  | HStatus hStatus, int32_t hSlot, HOutput const& hOutput) { | 
|  | converted = h2b(hStatus, &bStatus); | 
|  | if (!converted || bStatus != OK) { | 
|  | return; | 
|  | } | 
|  | *slot = hSlot; | 
|  | *outBufferAge = hOutput.bufferAge; | 
|  | bStatus = | 
|  | (hOutput.bufferNeedsReallocation ? | 
|  | BUFFER_NEEDS_REALLOCATION : 0) | | 
|  | (hOutput.releaseAllBuffers ? | 
|  | RELEASE_ALL_BUFFERS : 0); | 
|  | converted = h2b(hOutput.fence, fence); | 
|  | }); | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "dequeueBuffer: transaction failed."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | if (!converted) { | 
|  | LOG(ERROR) << "dequeueBuffer: corrupted transaction."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | return bStatus; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::detachBuffer(int slot) { | 
|  | status_t bStatus{}; | 
|  | Return<HStatus> transResult = mBase->detachBuffer( | 
|  | static_cast<int32_t>(slot)); | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "detachBuffer: transaction failed."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { | 
|  | LOG(ERROR) << "detachBuffer: corrupted transaction."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | return bStatus; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::detachNextBuffer( | 
|  | sp<GraphicBuffer>* outBuffer, sp<BFence>* outFence) { | 
|  | bool converted{}; | 
|  | status_t bStatus{}; | 
|  | Return<void> transResult = mBase->detachNextBuffer( | 
|  | [&converted, &bStatus, outBuffer, outFence] ( | 
|  | HStatus hStatus, | 
|  | HardwareBuffer const& hBuffer, | 
|  | hidl_handle const& hFence) { | 
|  | converted = h2b(hStatus, &bStatus) && | 
|  | h2b(hBuffer, outBuffer) && | 
|  | h2b(hFence, outFence); | 
|  | }); | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "detachNextBuffer: transaction failed."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | if (!converted) { | 
|  | LOG(ERROR) << "detachNextBuffer: corrupted transaction."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | return bStatus; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::attachBuffer( | 
|  | int* outSlot, sp<GraphicBuffer> const& buffer) { | 
|  | HardwareBuffer hBuffer{}; | 
|  | uint32_t hGenerationNumber{}; | 
|  | if (!b2h(buffer, &hBuffer, &hGenerationNumber)) { | 
|  | LOG(ERROR) << "attachBuffer: invalid input buffer."; | 
|  | return BAD_VALUE; | 
|  | } | 
|  |  | 
|  | bool converted{}; | 
|  | status_t bStatus{}; | 
|  | Return<void> transResult = mBase->attachBuffer(hBuffer, hGenerationNumber, | 
|  | [&converted, &bStatus, outSlot]( | 
|  | HStatus hStatus, int32_t hSlot, bool releaseAllBuffers) { | 
|  | converted = h2b(hStatus, &bStatus); | 
|  | *outSlot = static_cast<int>(hSlot); | 
|  | if (converted && releaseAllBuffers && bStatus == OK) { | 
|  | bStatus = IGraphicBufferProducer::RELEASE_ALL_BUFFERS; | 
|  | } | 
|  | }); | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "attachBuffer: transaction failed."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | if (!converted) { | 
|  | LOG(ERROR) << "attachBuffer: corrupted transaction."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | return bStatus; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::queueBuffer( | 
|  | int slot, | 
|  | QueueBufferInput const& input, | 
|  | QueueBufferOutput* output) { | 
|  | HQueueBufferInput hInput{}; | 
|  | HFenceWrapper hFenceWrapper; | 
|  | if (!b2h(input, &hInput, &hFenceWrapper)) { | 
|  | LOG(ERROR) << "queueBuffer: corrupted input."; | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | bool converted{}; | 
|  | status_t bStatus{}; | 
|  | Return<void> transResult = mBase->queueBuffer( | 
|  | static_cast<int32_t>(slot), | 
|  | hInput, | 
|  | [&converted, &bStatus, output]( | 
|  | HStatus hStatus, | 
|  | HQueueBufferOutput const& hOutput) { | 
|  | converted = h2b(hStatus, &bStatus) && h2b(hOutput, output); | 
|  | }); | 
|  |  | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "queueBuffer: transaction failed."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | if (!converted) { | 
|  | LOG(ERROR) << "queueBuffer: corrupted transaction."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | return bStatus; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::cancelBuffer(int slot, sp<BFence> const& fence) { | 
|  | HFenceWrapper hFenceWrapper; | 
|  | if (!b2h(fence, &hFenceWrapper)) { | 
|  | LOG(ERROR) << "cancelBuffer: corrupted input fence."; | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  | status_t bStatus{}; | 
|  | Return<HStatus> transResult = mBase->cancelBuffer( | 
|  | static_cast<int32_t>(slot), | 
|  | hFenceWrapper.getHandle()); | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "cancelBuffer: transaction failed."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { | 
|  | LOG(ERROR) << "cancelBuffer: corrupted transaction."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | return bStatus; | 
|  | } | 
|  |  | 
|  | int H2BGraphicBufferProducer::query(int what, int* value) { | 
|  | int result{}; | 
|  | Return<void> transResult = mBase->query( | 
|  | static_cast<int32_t>(what), | 
|  | [&result, value](int32_t r, int32_t v) { | 
|  | result = static_cast<int>(r); | 
|  | *value = static_cast<int>(v); | 
|  | }); | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "query: transaction failed."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::connect( | 
|  | sp<IProducerListener> const& listener, int api, | 
|  | bool producerControlledByApp, QueueBufferOutput* output) { | 
|  | HConnectionType hConnectionType; | 
|  | if (!b2h(api, &hConnectionType)) { | 
|  | LOG(ERROR) << "connect: corrupted input connection type."; | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  | sp<HProducerListener> hListener = nullptr; | 
|  | if (listener && listener->needsReleaseNotify()) { | 
|  | hListener = new B2HProducerListener(listener); | 
|  | if (!hListener) { | 
|  | LOG(ERROR) << "connect: failed to wrap listener."; | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool converted{}; | 
|  | status_t bStatus{}; | 
|  | Return<void> transResult = mBase->connect( | 
|  | hListener, | 
|  | hConnectionType, | 
|  | producerControlledByApp, | 
|  | [&converted, &bStatus, output]( | 
|  | HStatus hStatus, | 
|  | HQueueBufferOutput const& hOutput) { | 
|  | converted = h2b(hStatus, &bStatus) && h2b(hOutput, output); | 
|  | }); | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "connect: transaction failed."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | if (!converted) { | 
|  | LOG(ERROR) << "connect: corrupted transaction."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | return bStatus; | 
|  |  | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::disconnect(int api, DisconnectMode mode) { | 
|  | HConnectionType hConnectionType; | 
|  | if (mode == DisconnectMode::AllLocal) { | 
|  | hConnectionType = HConnectionType::CURRENTLY_CONNECTED; | 
|  | } else if (!b2h(api, &hConnectionType)) { | 
|  | LOG(ERROR) << "connect: corrupted input connection type."; | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | status_t bStatus{}; | 
|  | Return<HStatus> transResult = mBase->disconnect(hConnectionType); | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "disconnect: transaction failed."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { | 
|  | LOG(ERROR) << "disconnect: corrupted transaction."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | return bStatus; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::setSidebandStream( | 
|  | sp<NativeHandle> const& stream) { | 
|  | if (stream) { | 
|  | LOG(INFO) << "setSidebandStream: not supported."; | 
|  | return INVALID_OPERATION; | 
|  | } | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | void H2BGraphicBufferProducer::allocateBuffers( | 
|  | uint32_t width, uint32_t height, | 
|  | PixelFormat format, uint64_t usage) { | 
|  | status_t bStatus{}; | 
|  | Return<HStatus> transResult = mBase->allocateBuffers( | 
|  | width, height, static_cast<uint32_t>(format), usage); | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "allocateBuffer: transaction failed."; | 
|  | return; | 
|  | } | 
|  | if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { | 
|  | LOG(ERROR) << "allocateBuffer: corrupted transaction."; | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::allowAllocation(bool allow) { | 
|  | status_t bStatus{}; | 
|  | Return<HStatus> transResult = mBase->allowAllocation(allow); | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "allowAllocation: transaction failed."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { | 
|  | LOG(ERROR) << "allowAllocation: corrupted transaction."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | return bStatus; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::setGenerationNumber( | 
|  | uint32_t generationNumber) { | 
|  | status_t bStatus{}; | 
|  | Return<HStatus> transResult = mBase->setGenerationNumber(generationNumber); | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "setGenerationNumber: transaction failed."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { | 
|  | LOG(ERROR) << "setGenerationNumber: corrupted transaction."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | return bStatus; | 
|  | } | 
|  |  | 
|  | String8 H2BGraphicBufferProducer::getConsumerName() const { | 
|  | String8 bName; | 
|  | Return<void> transResult = mBase->getConsumerName( | 
|  | [&bName](hidl_string const& name) { | 
|  | bName = name.c_str(); | 
|  | }); | 
|  | return bName; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::setSharedBufferMode(bool sharedBufferMode) { | 
|  | if (sharedBufferMode) { | 
|  | LOG(INFO) << "setSharedBufferMode: not supported."; | 
|  | return INVALID_OPERATION; | 
|  | } | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::setAutoRefresh(bool autoRefresh) { | 
|  | if (autoRefresh) { | 
|  | LOG(INFO) << "setAutoRefresh: not supported."; | 
|  | return INVALID_OPERATION; | 
|  | } | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::setDequeueTimeout(nsecs_t timeout) { | 
|  | status_t bStatus{}; | 
|  | Return<HStatus> transResult = mBase->setDequeueTimeout( | 
|  | static_cast<int64_t>(timeout)); | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "setDequeueTimeout: transaction failed."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { | 
|  | LOG(ERROR) << "setDequeueTimeout: corrupted transaction."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | return bStatus; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::getLastQueuedBuffer( | 
|  | sp<GraphicBuffer>*, | 
|  | sp<BFence>*, | 
|  | float[16]) { | 
|  | LOG(INFO) << "getLastQueuedBuffer: not supported."; | 
|  | return INVALID_OPERATION; | 
|  | } | 
|  |  | 
|  | void H2BGraphicBufferProducer::getFrameTimestamps(FrameEventHistoryDelta*) { | 
|  | LOG(INFO) << "getFrameTimestamps: not supported."; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::getUniqueId(uint64_t* outId) const { | 
|  | Return<uint64_t> transResult = mBase->getUniqueId(); | 
|  | if (!transResult.isOk()) { | 
|  | LOG(ERROR) << "getUniqueId: transaction failed."; | 
|  | return FAILED_TRANSACTION; | 
|  | } | 
|  | *outId = static_cast<uint64_t>(transResult); | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | status_t H2BGraphicBufferProducer::getConsumerUsage(uint64_t*) const { | 
|  | LOG(INFO) << "getConsumerUsage: not supported."; | 
|  | return INVALID_OPERATION; | 
|  | } | 
|  |  | 
|  | }  // namespace utils | 
|  | }  // namespace V2_0 | 
|  | }  // namespace bufferqueue | 
|  | }  // namespace graphics | 
|  | }  // namespace hardware | 
|  | }  // namespace android |