From ab8443c81b92a0fd06b3e716972b86735926b5a4 Mon Sep 17 00:00:00 2001 From: Carlos Martinez Romero Date: Wed, 3 Jul 2024 13:50:10 -0700 Subject: Use SurfaceListener instead of IProducerListener IProducerListener is being deprecated outside of libgui, we are limiting IGBP/IGBC usage outside of libgui to allow for future changes. See go/warren-buffers for more information. Bug: 342199105 Test: Pending FLAG: EXEMPT no op refactor BYPASS_IGBP_IGBC_API_REASON: Refactor Change-Id: I37129c645b55fd33d6789b9b2ba85a35e044dcd9 --- libs/gui/Surface.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'libs/gui/Surface.cpp') diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 87fd448f0c..685391effc 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -44,8 +44,6 @@ #include #include -#include - #include #include #include @@ -1860,30 +1858,31 @@ bool Surface::transformToDisplayInverse() const { } int Surface::connect(int api) { - static sp listener = new StubProducerListener(); + static sp listener = new StubSurfaceListener(); return connect(api, listener); } -int Surface::connect(int api, const sp& listener) { +int Surface::connect(int api, const sp& listener) { return connect(api, listener, false); } int Surface::connect( int api, bool reportBufferRemoval, const sp& sListener) { - if (sListener != nullptr) { - mListenerProxy = new ProducerListenerProxy(this, sListener); - } - return connect(api, mListenerProxy, reportBufferRemoval); + return connect(api, sListener, reportBufferRemoval); } -int Surface::connect( - int api, const sp& listener, bool reportBufferRemoval) { +int Surface::connect(int api, const sp& listener, bool reportBufferRemoval) { ATRACE_CALL(); ALOGV("Surface::connect"); Mutex::Autolock lock(mMutex); IGraphicBufferProducer::QueueBufferOutput output; mReportRemovedBuffers = reportBufferRemoval; - int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output); + if (listener != nullptr) { + mListenerProxy = new ProducerListenerProxy(this, listener); + } + + int err = + mGraphicBufferProducer->connect(mListenerProxy, api, mProducerControlledByApp, &output); if (err == NO_ERROR) { mDefaultWidth = output.width; mDefaultHeight = output.height; @@ -1911,7 +1910,6 @@ int Surface::connect( return err; } - int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) { ATRACE_CALL(); ALOGV("Surface::disconnect"); -- cgit v1.2.3-59-g8ed1b From 0088639895b6011293cafd2ed421ff14eccf42f7 Mon Sep 17 00:00:00 2001 From: Jim Shargo Date: Fri, 12 Jul 2024 22:12:20 +0000 Subject: Surface: Provide more useful/platform-y buffer methods These will be easier to use than having to use ANativeWindow equivalents. BYPASS_IGBP_IGBC_API_REASON=warren buffers Flag: com.android.graphics.libgui.flags.wb_platform_api_improvements Bug: 340933794 Test: new libgui_test tests, atest Change-Id: I0343abb45aadf708ef6115fa108a3b4c402cfd47 --- libs/gui/Surface.cpp | 46 +++++++++++++++++++++++++++++ libs/gui/include/gui/Surface.h | 17 +++++++++++ libs/gui/tests/Android.bp | 1 + libs/gui/tests/Surface_test.cpp | 65 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 128 insertions(+), 1 deletion(-) (limited to 'libs/gui/Surface.cpp') diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 685391effc..97c0299166 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -21,6 +21,8 @@ #include #include +#include +#include #include #include #include @@ -693,6 +695,50 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { return OK; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + +status_t Surface::dequeueBuffer(sp* buffer, sp* outFence) { + if (buffer == nullptr || outFence == nullptr) { + return BAD_VALUE; + } + + android_native_buffer_t* anb; + int fd = -1; + status_t res = dequeueBuffer(&anb, &fd); + *buffer = GraphicBuffer::from(anb); + *outFence = sp::make(fd); + return res; +} + +status_t Surface::queueBuffer(const sp& buffer, const sp& fd) { + if (buffer == nullptr) { + return BAD_VALUE; + } + return queueBuffer(buffer.get(), fd ? fd->get() : -1); +} + +status_t Surface::detachBuffer(const sp& buffer) { + if (nullptr == buffer) { + return BAD_VALUE; + } + + Mutex::Autolock lock(mMutex); + + uint64_t bufferId = buffer->getId(); + for (int slot = 0; slot < Surface::NUM_BUFFER_SLOTS; ++slot) { + auto& bufferSlot = mSlots[slot]; + if (bufferSlot.buffer != nullptr && bufferSlot.buffer->getId() == bufferId) { + bufferSlot.buffer = nullptr; + bufferSlot.dirtyRegion = Region::INVALID_REGION; + return mGraphicBufferProducer->detachBuffer(slot); + } + } + + return BAD_VALUE; +} + +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + int Surface::dequeueBuffers(std::vector* buffers) { using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput; using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput; diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 124550113e..c470b2f873 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -18,6 +18,7 @@ #define ANDROID_GUI_SURFACE_H #include +#include #include #include #include @@ -35,6 +36,8 @@ namespace android { +class GraphicBuffer; + namespace gui { class ISurfaceComposer; } // namespace gui @@ -395,6 +398,20 @@ public: static status_t attachAndQueueBufferWithDataspace(Surface* surface, sp buffer, ui::Dataspace dataspace); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + // Dequeues a buffer and its outFence, which must be signalled before the buffer can be used. + status_t dequeueBuffer(sp* buffer, sp* outFence); + + // Queues a buffer, with an optional fd fence that captures pending work on the buffer. This + // buffer must have been returned by dequeueBuffer or associated with this Surface via an + // attachBuffer operation. + status_t queueBuffer(const sp& buffer, const sp& fd = Fence::NO_FENCE); + + // Detaches this buffer, dissociating it from this Surface. This buffer must have been returned + // by queueBuffer or associated with this Surface via an attachBuffer operation. + status_t detachBuffer(const sp& buffer); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + // Batch version of dequeueBuffer, cancelBuffer and queueBuffer // Note that these batched operations are not supported when shared buffer mode is being used. struct BatchBuffer { diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index daaddfbc15..9558edab87 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -25,6 +25,7 @@ cc_test { "-Wthread-safety", "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_SETFRAMERATE=true", "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_EXTENDEDALLOCATE=true", + "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_PLATFORM_API_IMPROVEMENTS=true", ], srcs: [ diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 88168e3955..0aa521699f 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -24,23 +24,26 @@ #include #include #include +#include #include #include #include +#include #include #include #include #include -#include #include #include #include #include #include +#include #include #include #include +#include #include #include @@ -2225,4 +2228,64 @@ TEST_F(SurfaceTest, BatchIllegalOperations) { ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + +TEST_F(SurfaceTest, PlatformBufferMethods) { + sp producer; + sp consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp cpuConsumer = sp::make(consumer, 1); + sp surface = sp::make(producer); + sp listener = sp::make(); + sp buffer; + sp fence; + + EXPECT_EQ(OK, + surface->connect(NATIVE_WINDOW_API_CPU, listener, /* reportBufferRemoval */ false)); + + // + // Verify nullptrs are handled safely: + // + + EXPECT_EQ(BAD_VALUE, surface->dequeueBuffer((sp*)nullptr, nullptr)); + EXPECT_EQ(BAD_VALUE, surface->dequeueBuffer((sp*)nullptr, &fence)); + EXPECT_EQ(BAD_VALUE, surface->dequeueBuffer(&buffer, nullptr)); + EXPECT_EQ(BAD_VALUE, surface->queueBuffer(nullptr, nullptr)); + EXPECT_EQ(BAD_VALUE, surface->detachBuffer(nullptr)); + + // + // Verify dequeue/queue: + // + + EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)); + EXPECT_NE(nullptr, buffer); + EXPECT_EQ(OK, surface->queueBuffer(buffer, fence)); + + // + // Verify dequeue/detach: + // + + wp weakBuffer; + { + EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)); + + EXPECT_EQ(OK, surface->detachBuffer(buffer)); + + weakBuffer = buffer; + buffer = nullptr; + } + EXPECT_EQ(nullptr, weakBuffer.promote()) << "Weak buffer still held by Surface."; + + // + // Verify detach without borrowing the buffer does not work: + // + + sp heldTooLongBuffer; + EXPECT_EQ(OK, surface->dequeueBuffer(&heldTooLongBuffer, &fence)); + EXPECT_EQ(OK, surface->queueBuffer(heldTooLongBuffer)); + EXPECT_EQ(BAD_VALUE, surface->detachBuffer(heldTooLongBuffer)); +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + } // namespace android -- cgit v1.2.3-59-g8ed1b From e77d47e548588a5bdd6ed7b8c8c88610cc501b3e Mon Sep 17 00:00:00 2001 From: Jim Shargo Date: Wed, 17 Jul 2024 21:29:08 +0000 Subject: Surface: expose allowAllocation method This is required to make some clients of IGBP work with Surface. See go/warren-buffers for more details. BYPASS_IGBP_IGBC_API_REASON=warren buffers Bug: 340933794 Flag: com.android.graphics.libgui.flags.wb_platform_api_improvements Test: new test Change-Id: I4a3150c9732fdeec2277457c5c476db657bb2299 --- libs/gui/Surface.cpp | 6 ++++++ libs/gui/include/gui/Surface.h | 5 +++++ libs/gui/tests/Surface_test.cpp | 26 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) (limited to 'libs/gui/Surface.cpp') diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 97c0299166..eb9faf584b 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -163,6 +163,12 @@ void Surface::allocateBuffers() { mReqFormat, mReqUsage); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +status_t Surface::allowAllocation(bool allowAllocation) { + return mGraphicBufferProducer->allowAllocation(allowAllocation); +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + status_t Surface::setGenerationNumber(uint32_t generation) { status_t result = mGraphicBufferProducer->setGenerationNumber(generation); if (result == NO_ERROR) { diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index c470b2f873..d07e121d6e 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -167,6 +167,11 @@ public: */ virtual void allocateBuffers(); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + // See IGraphicBufferProducer::allowAllocation + status_t allowAllocation(bool allowAllocation); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + /* Sets the generation number on the IGraphicBufferProducer and updates the * generation number on any buffers attached to the Surface after this call. * See IGBP::setGenerationNumber for more information. */ diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 0aa521699f..8d6917f3a6 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -2286,6 +2286,32 @@ TEST_F(SurfaceTest, PlatformBufferMethods) { EXPECT_EQ(OK, surface->queueBuffer(heldTooLongBuffer)); EXPECT_EQ(BAD_VALUE, surface->detachBuffer(heldTooLongBuffer)); } + +TEST_F(SurfaceTest, AllowAllocation) { + sp producer; + sp consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + // controlledByApp must be true to disable blocking + sp cpuConsumer = sp::make(consumer, 1, /*controlledByApp*/ true); + sp surface = sp::make(producer, /*controlledByApp*/ true); + sp listener = sp::make(); + sp buffer; + sp fence; + + EXPECT_EQ(OK, + surface->connect(NATIVE_WINDOW_API_CPU, listener, /* reportBufferRemoval */ false)); + EXPECT_EQ(OK, surface->allowAllocation(false)); + + EXPECT_EQ(OK, surface->setDequeueTimeout(-1)); + EXPECT_EQ(WOULD_BLOCK, surface->dequeueBuffer(&buffer, &fence)); + + EXPECT_EQ(OK, surface->setDequeueTimeout(10)); + EXPECT_EQ(TIMED_OUT, surface->dequeueBuffer(&buffer, &fence)); + + EXPECT_EQ(OK, surface->allowAllocation(true)); + EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)); +} #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) } // namespace android -- cgit v1.2.3-59-g8ed1b From c604993d37f29bcf14f0bb11d234db0e3442b533 Mon Sep 17 00:00:00 2001 From: Jim Shargo Date: Mon, 8 Jul 2024 22:40:59 +0000 Subject: Surface: don't hold mMutex when queuing buffers This is the behavior of the underlying IGBP, but can cause deadlocks in certain situations dependend on by some clients. BYPASS_IGBP_IGBC_API_REASON=warren buffers Bug: 340933794 Flag: com.android.graphics.libgui.flags.wb_platform_api_improvements Test: new test in `atest libgui_test` Change-Id: I5a02e6e12980d211e70ae4db8cbd2344e323d613 --- libs/gui/Surface.cpp | 113 ++++++++++++++++++++++++++++++++++++++++ libs/gui/tests/Surface_test.cpp | 76 +++++++++++++++++++++++++++ 2 files changed, 189 insertions(+) (limited to 'libs/gui/Surface.cpp') diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index eb9faf584b..a98a3c0850 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -1168,6 +1168,117 @@ void Surface::onBufferQueuedLocked(int slot, sp fence, } } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + +int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { + ATRACE_CALL(); + ALOGV("Surface::queueBuffer"); + + IGraphicBufferProducer::QueueBufferOutput output; + IGraphicBufferProducer::QueueBufferInput input; + int slot; + sp fence; + { + Mutex::Autolock lock(mMutex); + + slot = getSlotFromBufferLocked(buffer); + if (slot < 0) { + if (fenceFd >= 0) { + close(fenceFd); + } + return slot; + } + if (mSharedBufferSlot == slot && mSharedBufferHasBeenQueued) { + if (fenceFd >= 0) { + close(fenceFd); + } + return OK; + } + + getQueueBufferInputLocked(buffer, fenceFd, mTimestamp, &input); + applyGrallocMetadataLocked(buffer, input); + fence = input.fence; + } + nsecs_t now = systemTime(); + // Drop the lock temporarily while we touch the underlying producer. In the case of a local + // BufferQueue, the following should be allowable: + // + // Surface::queueBuffer + // -> IConsumerListener::onFrameAvailable callback triggers automatically + // -> implementation calls IGraphicBufferConsumer::acquire/release immediately + // -> SurfaceListener::onBufferRelesed callback triggers automatically + // -> implementation calls Surface::dequeueBuffer + status_t err = mGraphicBufferProducer->queueBuffer(slot, input, &output); + { + Mutex::Autolock lock(mMutex); + + mLastQueueDuration = systemTime() - now; + if (err != OK) { + ALOGE("queueBuffer: error queuing buffer, %d", err); + } + + onBufferQueuedLocked(slot, fence, output); + } + + return err; +} + +int Surface::queueBuffers(const std::vector& buffers) { + ATRACE_CALL(); + ALOGV("Surface::queueBuffers"); + + size_t numBuffers = buffers.size(); + std::vector queueBufferInputs(numBuffers); + std::vector queueBufferOutputs; + std::vector bufferSlots(numBuffers, -1); + std::vector> bufferFences(numBuffers); + + int err; + { + Mutex::Autolock lock(mMutex); + + if (mSharedBufferMode) { + ALOGE("%s: batched operation is not supported in shared buffer mode", __FUNCTION__); + return INVALID_OPERATION; + } + + for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) { + int i = getSlotFromBufferLocked(buffers[batchIdx].buffer); + if (i < 0) { + if (buffers[batchIdx].fenceFd >= 0) { + close(buffers[batchIdx].fenceFd); + } + return i; + } + bufferSlots[batchIdx] = i; + + IGraphicBufferProducer::QueueBufferInput input; + getQueueBufferInputLocked(buffers[batchIdx].buffer, buffers[batchIdx].fenceFd, + buffers[batchIdx].timestamp, &input); + bufferFences[batchIdx] = input.fence; + queueBufferInputs[batchIdx] = input; + } + } + nsecs_t now = systemTime(); + err = mGraphicBufferProducer->queueBuffers(queueBufferInputs, &queueBufferOutputs); + { + Mutex::Autolock lock(mMutex); + mLastQueueDuration = systemTime() - now; + if (err != OK) { + ALOGE("%s: error queuing buffer, %d", __FUNCTION__, err); + } + + for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) { + onBufferQueuedLocked(bufferSlots[batchIdx], bufferFences[batchIdx], + queueBufferOutputs[batchIdx]); + } + } + + return err; +} + +#else + int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { ATRACE_CALL(); ALOGV("Surface::queueBuffer"); @@ -1255,6 +1366,8 @@ int Surface::queueBuffers(const std::vector& buffers) { return err; } +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + void Surface::querySupportedTimestampsLocked() const { // mMutex must be locked when calling this method. diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 8d6917f3a6..ee0c555fcb 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -23,12 +23,17 @@ #include #include #include +#include #include #include #include #include #include +#include #include +#include +#include +#include #include #include #include @@ -36,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -2312,6 +2318,76 @@ TEST_F(SurfaceTest, AllowAllocation) { EXPECT_EQ(OK, surface->allowAllocation(true)); EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)); } + +TEST_F(SurfaceTest, QueueAcquireReleaseDequeue_CalledInStack_DoesNotDeadlock) { + class DequeuingSurfaceListener : public SurfaceListener { + public: + DequeuingSurfaceListener(const wp& surface) : mSurface(surface) {} + + virtual void onBufferReleased() override { + sp surface = mSurface.promote(); + ASSERT_NE(nullptr, surface); + EXPECT_EQ(OK, surface->dequeueBuffer(&mBuffer, &mFence)); + } + + virtual bool needsReleaseNotify() override { return true; } + virtual void onBuffersDiscarded(const std::vector>&) override {} + virtual void onBufferDetached(int) override {} + + sp mBuffer; + sp mFence; + + private: + wp mSurface; + }; + + class ImmediateReleaseConsumerListener : public BufferItemConsumer::FrameAvailableListener { + public: + ImmediateReleaseConsumerListener(const wp& consumer) + : mConsumer(consumer) {} + + virtual void onFrameAvailable(const BufferItem&) override { + sp consumer = mConsumer.promote(); + ASSERT_NE(nullptr, consumer); + + mCalls += 1; + + BufferItem buffer; + EXPECT_EQ(OK, consumer->acquireBuffer(&buffer, 0)); + EXPECT_EQ(OK, consumer->releaseBuffer(buffer)); + } + + size_t mCalls = 0; + + private: + wp mConsumer; + }; + + sp bqProducer; + sp bqConsumer; + BufferQueue::createBufferQueue(&bqProducer, &bqConsumer); + + sp consumer = sp::make(bqConsumer, 3); + sp surface = sp::make(bqProducer); + sp consumerListener = + sp::make(consumer); + consumer->setFrameAvailableListener(consumerListener); + + sp surfaceListener = sp::make(surface); + EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, false, surfaceListener)); + + EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(2)); + + sp buffer; + sp fence; + EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)); + EXPECT_EQ(OK, surface->queueBuffer(buffer, fence)); + + EXPECT_EQ(1u, consumerListener->mCalls); + EXPECT_NE(nullptr, surfaceListener->mBuffer); + + EXPECT_EQ(OK, surface->disconnect(NATIVE_WINDOW_API_CPU)); +} #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) } // namespace android -- cgit v1.2.3-59-g8ed1b From 6a5fdc1d5f6f76c5938aa73f72941bd3f2643628 Mon Sep 17 00:00:00 2001 From: Carlos Martinez Romero Date: Mon, 22 Jul 2024 09:40:33 -0700 Subject: Remove redundant connect methods in Surface. Test: Surface_test Bug: 354273690 Flag: EXEMPT - strict mechanical refactor. We were going to add one but decided not to. Change-Id: I8c4cc702726e12b169fc4fa0b158623d0a47913a --- libs/gui/Surface.cpp | 9 --------- libs/gui/include/gui/Surface.h | 11 +++-------- libs/gui/tests/Surface_test.cpp | 18 +++++++++--------- 3 files changed, 12 insertions(+), 26 deletions(-) (limited to 'libs/gui/Surface.cpp') diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index a98a3c0850..4a8df9e1bb 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -2027,15 +2027,6 @@ int Surface::connect(int api) { return connect(api, listener); } -int Surface::connect(int api, const sp& listener) { - return connect(api, listener, false); -} - -int Surface::connect( - int api, bool reportBufferRemoval, const sp& sListener) { - return connect(api, sListener, reportBufferRemoval); -} - int Surface::connect(int api, const sp& listener, bool reportBufferRemoval) { ATRACE_CALL(); ALOGV("Surface::connect"); diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 3e2aa474f7..39207f835f 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -381,20 +381,15 @@ public: virtual int unlockAndPost(); virtual int query(int what, int* value) const; - virtual int connect(int api, const sp& listener); - // When reportBufferRemoval is true, clients must call getAndFlushRemovedBuffers to fetch // GraphicBuffers removed from this surface after a dequeueBuffer, detachNextBuffer or // attachBuffer call. This allows clients with their own buffer caches to free up buffers no // longer in use by this surface. - virtual int connect(int api, const sp& listener, bool reportBufferRemoval); - virtual int detachNextBuffer(sp* outBuffer, - sp* outFence); + virtual int connect(int api, const sp& listener, + bool reportBufferRemoval = false); + virtual int detachNextBuffer(sp* outBuffer, sp* outFence); virtual int attachBuffer(ANativeWindowBuffer*); - virtual int connect( - int api, bool reportBufferRemoval, - const sp& sListener); virtual void destroy(); // When client connects to Surface with reportBufferRemoval set to true, any buffers removed diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index ee0c555fcb..85f4a42b89 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -151,10 +151,10 @@ protected: if (hasSurfaceListener) { listener = new FakeSurfaceListener(enableReleasedCb); } - ASSERT_EQ(OK, surface->connect( - NATIVE_WINDOW_API_CPU, - /*reportBufferRemoval*/true, - /*listener*/listener)); + ASSERT_EQ(OK, + surface->connect(NATIVE_WINDOW_API_CPU, + /*listener*/ listener, + /*reportBufferRemoval*/ true)); const int BUFFER_COUNT = 4 + extraDiscardedBuffers; ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS)); @@ -500,10 +500,10 @@ TEST_F(SurfaceTest, GetAndFlushRemovedBuffers) { sp surface = new Surface(producer); sp window(surface); sp listener = new StubSurfaceListener(); - ASSERT_EQ(OK, surface->connect( - NATIVE_WINDOW_API_CPU, - /*listener*/listener, - /*reportBufferRemoval*/true)); + ASSERT_EQ(OK, + surface->connect(NATIVE_WINDOW_API_CPU, + /*listener*/ listener, + /*reportBufferRemoval*/ true)); const int BUFFER_COUNT = 4; ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS)); @@ -2374,7 +2374,7 @@ TEST_F(SurfaceTest, QueueAcquireReleaseDequeue_CalledInStack_DoesNotDeadlock) { consumer->setFrameAvailableListener(consumerListener); sp surfaceListener = sp::make(surface); - EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, false, surfaceListener)); + EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener, false)); EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(2)); -- cgit v1.2.3-59-g8ed1b From 924f9500f771a93a7a45bd92bf0d1bd0d8b8e583 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Thu, 15 Aug 2024 20:01:08 +0000 Subject: Rename AidlStatusUtil.h to AidlUtil.h Shoving some utility methods to help with transitioning between AIDL types and the types we use internally Bug: 329465218 Flag: EXEMPT mechanical refactor Test: builds Change-Id: I07cdfb7c6bfe89e42c1eb7169e15329916df448a --- libs/gui/Surface.cpp | 2 +- libs/gui/SurfaceComposerClient.cpp | 2 +- libs/gui/WindowInfosListenerReporter.cpp | 2 +- libs/gui/include/gui/AidlStatusUtil.h | 138 --------------------- libs/gui/include/gui/AidlUtil.h | 138 +++++++++++++++++++++ libs/gui/tests/BLASTBufferQueue_test.cpp | 2 +- libs/gui/tests/RegionSampling_test.cpp | 2 +- libs/gui/tests/Surface_test.cpp | 2 +- libs/gui/tests/TestServer_test.cpp | 2 +- services/surfaceflinger/Client.cpp | 2 +- services/surfaceflinger/SurfaceFlinger.cpp | 2 +- .../surfaceflinger/tests/BootDisplayMode_test.cpp | 2 +- services/surfaceflinger/tests/Credentials_test.cpp | 2 +- .../surfaceflinger/tests/LayerTransactionTest.h | 2 +- .../tests/LayerTypeTransaction_test.cpp | 2 +- services/surfaceflinger/tests/MirrorLayer_test.cpp | 2 +- .../surfaceflinger/tests/ScreenCapture_test.cpp | 2 +- .../surfaceflinger/tests/TextureFiltering_test.cpp | 2 +- .../SurfaceFlinger_HdrOutputControlTest.cpp | 2 +- .../surfaceflinger/tests/utils/ScreenshotUtils.h | 2 +- 20 files changed, 156 insertions(+), 156 deletions(-) delete mode 100644 libs/gui/include/gui/AidlStatusUtil.h create mode 100644 libs/gui/include/gui/AidlUtil.h (limited to 'libs/gui/Surface.cpp') diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 4a8df9e1bb..da3886ccf0 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -43,7 +43,7 @@ #include #include -#include +#include #include #include diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index b5d9366185..7d3e5c166f 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -43,7 +43,7 @@ #include -#include +#include #include #include #include diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp index 0929b8e120..91c9a85149 100644 --- a/libs/gui/WindowInfosListenerReporter.cpp +++ b/libs/gui/WindowInfosListenerReporter.cpp @@ -15,7 +15,7 @@ */ #include -#include +#include #include #include "gui/WindowInfosUpdate.h" diff --git a/libs/gui/include/gui/AidlStatusUtil.h b/libs/gui/include/gui/AidlStatusUtil.h deleted file mode 100644 index 159e0329ac..0000000000 --- a/libs/gui/include/gui/AidlStatusUtil.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2022 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. - */ - -#pragma once - -#include -#include -#include - -// Originally extracted from frameworks/av/media/libaudioclient/include/media/AidlConversionUtil.h -namespace android::gui::aidl_utils { - -/** - * Return the equivalent Android status_t from a binder exception code. - * - * Generally one should use statusTFromBinderStatus() instead. - * - * Exception codes can be generated from a remote Java service exception, translate - * them for use on the Native side. - * - * Note: for EX_TRANSACTION_FAILED and EX_SERVICE_SPECIFIC a more detailed error code - * can be found from transactionError() or serviceSpecificErrorCode(). - */ -static inline status_t statusTFromExceptionCode(int32_t exceptionCode) { - using namespace ::android::binder; - switch (exceptionCode) { - case Status::EX_NONE: - return OK; - case Status::EX_SECURITY: // Java SecurityException, rethrows locally in Java - return PERMISSION_DENIED; - case Status::EX_BAD_PARCELABLE: // Java BadParcelableException, rethrows in Java - case Status::EX_ILLEGAL_ARGUMENT: // Java IllegalArgumentException, rethrows in Java - case Status::EX_NULL_POINTER: // Java NullPointerException, rethrows in Java - return BAD_VALUE; - case Status::EX_ILLEGAL_STATE: // Java IllegalStateException, rethrows in Java - case Status::EX_UNSUPPORTED_OPERATION: // Java UnsupportedOperationException, rethrows - return INVALID_OPERATION; - case Status::EX_HAS_REPLY_HEADER: // Native strictmode violation - case Status::EX_PARCELABLE: // Java bootclass loader (not standard exception), rethrows - case Status::EX_NETWORK_MAIN_THREAD: // Java NetworkOnMainThreadException, rethrows - case Status::EX_TRANSACTION_FAILED: // Native - see error code - case Status::EX_SERVICE_SPECIFIC: // Java ServiceSpecificException, - // rethrows in Java with integer error code - return UNKNOWN_ERROR; - } - return UNKNOWN_ERROR; -} - -/** - * Return the equivalent Android status_t from a binder status. - * - * Used to handle errors from a AIDL method declaration - * - * [oneway] void method(type0 param0, ...) - * - * or the following (where return_type is not a status_t) - * - * return_type method(type0 param0, ...) - */ -static inline status_t statusTFromBinderStatus(const ::android::binder::Status &status) { - return status.isOk() ? OK // check OK, - : status.serviceSpecificErrorCode() // service-side error, not standard Java exception - // (fromServiceSpecificError) - ?: status.transactionError() // a native binder transaction error (fromStatusT) - ?: statusTFromExceptionCode(status.exceptionCode()); // a service-side error with a - // standard Java exception (fromExceptionCode) -} - -/** - * Return a binder::Status from native service status. - * - * This is used for methods not returning an explicit status_t, - * where Java callers expect an exception, not an integer return value. - */ -static inline ::android::binder::Status binderStatusFromStatusT( - status_t status, const char *optionalMessage = nullptr) { - const char *const emptyIfNull = optionalMessage == nullptr ? "" : optionalMessage; - // From binder::Status instructions: - // Prefer a generic exception code when possible, then a service specific - // code, and finally a status_t for low level failures or legacy support. - // Exception codes and service specific errors map to nicer exceptions for - // Java clients. - - using namespace ::android::binder; - switch (status) { - case OK: - return Status::ok(); - case PERMISSION_DENIED: // throw SecurityException on Java side - return Status::fromExceptionCode(Status::EX_SECURITY, emptyIfNull); - case BAD_VALUE: // throw IllegalArgumentException on Java side - return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, emptyIfNull); - case INVALID_OPERATION: // throw IllegalStateException on Java side - return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, emptyIfNull); - } - - // A service specific error will not show on status.transactionError() so - // be sure to use statusTFromBinderStatus() for reliable error handling. - - // throw a ServiceSpecificException. - return Status::fromServiceSpecificError(status, emptyIfNull); -} - -static inline Rect fromARect(ARect rect) { - return Rect(rect.left, rect.top, rect.right, rect.bottom); -} - -static inline ARect toARect(Rect rect) { - ARect aRect; - - aRect.left = rect.left; - aRect.top = rect.top; - aRect.right = rect.right; - aRect.bottom = rect.bottom; - return aRect; -} - -static inline ARect toARect(int32_t left, int32_t top, int32_t right, int32_t bottom) { - return toARect(Rect(left, top, right, bottom)); -} - -static inline ARect toARect(int32_t width, int32_t height) { - return toARect(Rect(width, height)); -} - -} // namespace android::gui::aidl_utils diff --git a/libs/gui/include/gui/AidlUtil.h b/libs/gui/include/gui/AidlUtil.h new file mode 100644 index 0000000000..a3ecd84ba1 --- /dev/null +++ b/libs/gui/include/gui/AidlUtil.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2022 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. + */ + +#pragma once + +#include +#include +#include + +// Originally extracted from frameworks/av/media/libaudioclient/include/media/AidlConversionUtil.h +namespace android::gui::aidl_utils { + +/** + * Return the equivalent Android status_t from a binder exception code. + * + * Generally one should use statusTFromBinderStatus() instead. + * + * Exception codes can be generated from a remote Java service exception, translate + * them for use on the Native side. + * + * Note: for EX_TRANSACTION_FAILED and EX_SERVICE_SPECIFIC a more detailed error code + * can be found from transactionError() or serviceSpecificErrorCode(). + */ +static inline status_t statusTFromExceptionCode(int32_t exceptionCode) { + using namespace ::android::binder; + switch (exceptionCode) { + case Status::EX_NONE: + return OK; + case Status::EX_SECURITY: // Java SecurityException, rethrows locally in Java + return PERMISSION_DENIED; + case Status::EX_BAD_PARCELABLE: // Java BadParcelableException, rethrows in Java + case Status::EX_ILLEGAL_ARGUMENT: // Java IllegalArgumentException, rethrows in Java + case Status::EX_NULL_POINTER: // Java NullPointerException, rethrows in Java + return BAD_VALUE; + case Status::EX_ILLEGAL_STATE: // Java IllegalStateException, rethrows in Java + case Status::EX_UNSUPPORTED_OPERATION: // Java UnsupportedOperationException, rethrows + return INVALID_OPERATION; + case Status::EX_HAS_REPLY_HEADER: // Native strictmode violation + case Status::EX_PARCELABLE: // Java bootclass loader (not standard exception), rethrows + case Status::EX_NETWORK_MAIN_THREAD: // Java NetworkOnMainThreadException, rethrows + case Status::EX_TRANSACTION_FAILED: // Native - see error code + case Status::EX_SERVICE_SPECIFIC: // Java ServiceSpecificException, + // rethrows in Java with integer error code + return UNKNOWN_ERROR; + } + return UNKNOWN_ERROR; +} + +/** + * Return the equivalent Android status_t from a binder status. + * + * Used to handle errors from a AIDL method declaration + * + * [oneway] void method(type0 param0, ...) + * + * or the following (where return_type is not a status_t) + * + * return_type method(type0 param0, ...) + */ +static inline status_t statusTFromBinderStatus(const ::android::binder::Status& status) { + return status.isOk() ? OK // check OK, + : status.serviceSpecificErrorCode() // service-side error, not standard Java exception + // (fromServiceSpecificError) + ?: status.transactionError() // a native binder transaction error (fromStatusT) + ?: statusTFromExceptionCode(status.exceptionCode()); // a service-side error with a + // standard Java exception (fromExceptionCode) +} + +/** + * Return a binder::Status from native service status. + * + * This is used for methods not returning an explicit status_t, + * where Java callers expect an exception, not an integer return value. + */ +static inline ::android::binder::Status binderStatusFromStatusT( + status_t status, const char* optionalMessage = nullptr) { + const char* const emptyIfNull = optionalMessage == nullptr ? "" : optionalMessage; + // From binder::Status instructions: + // Prefer a generic exception code when possible, then a service specific + // code, and finally a status_t for low level failures or legacy support. + // Exception codes and service specific errors map to nicer exceptions for + // Java clients. + + using namespace ::android::binder; + switch (status) { + case OK: + return Status::ok(); + case PERMISSION_DENIED: // throw SecurityException on Java side + return Status::fromExceptionCode(Status::EX_SECURITY, emptyIfNull); + case BAD_VALUE: // throw IllegalArgumentException on Java side + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, emptyIfNull); + case INVALID_OPERATION: // throw IllegalStateException on Java side + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, emptyIfNull); + } + + // A service specific error will not show on status.transactionError() so + // be sure to use statusTFromBinderStatus() for reliable error handling. + + // throw a ServiceSpecificException. + return Status::fromServiceSpecificError(status, emptyIfNull); +} + +static inline Rect fromARect(ARect rect) { + return Rect(rect.left, rect.top, rect.right, rect.bottom); +} + +static inline ARect toARect(Rect rect) { + ARect aRect; + + aRect.left = rect.left; + aRect.top = rect.top; + aRect.right = rect.right; + aRect.bottom = rect.bottom; + return aRect; +} + +static inline ARect toARect(int32_t left, int32_t top, int32_t right, int32_t bottom) { + return toARect(Rect(left, top, right, bottom)); +} + +static inline ARect toARect(int32_t width, int32_t height) { + return toARect(Rect(width, height)); +} + +} // namespace android::gui::aidl_utils diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 46a19c273d..eb2a61d151 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include #include diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp index 223e4b6cbd..a0d8c53385 100644 --- a/libs/gui/tests/RegionSampling_test.cpp +++ b/libs/gui/tests/RegionSampling_test.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include #include diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 4232443308..ab09dfc58d 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/libs/gui/tests/TestServer_test.cpp b/libs/gui/tests/TestServer_test.cpp index 8712988e52..d6407820ff 100644 --- a/libs/gui/tests/TestServer_test.cpp +++ b/libs/gui/tests/TestServer_test.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp index 6b4215e2f4..abeb2a92eb 100644 --- a/services/surfaceflinger/Client.cpp +++ b/services/surfaceflinger/Client.cpp @@ -21,7 +21,7 @@ #include -#include +#include #include #include "Client.h" diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 1e374c0a05..e8d64d92ae 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -64,7 +64,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/services/surfaceflinger/tests/BootDisplayMode_test.cpp b/services/surfaceflinger/tests/BootDisplayMode_test.cpp index 4f41a81011..222642f50c 100644 --- a/services/surfaceflinger/tests/BootDisplayMode_test.cpp +++ b/services/surfaceflinger/tests/BootDisplayMode_test.cpp @@ -18,7 +18,7 @@ #include -#include +#include #include #include #include diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp index babbcd4e35..e6fed63d96 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include #include diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h index 5b056d0765..03f900526d 100644 --- a/services/surfaceflinger/tests/LayerTransactionTest.h +++ b/services/surfaceflinger/tests/LayerTransactionTest.h @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include #include diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp index e655df7695..76bae4116c 100644 --- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp @@ -15,10 +15,10 @@ */ // TODO(b/129481165): remove the #pragma below and fix conversion issues -#include "gui/AidlStatusUtil.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" +#include #include #include #include "TransactionTestHarnesses.h" diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp index 2a535881b2..6cc1c51cde 100644 --- a/services/surfaceflinger/tests/MirrorLayer_test.cpp +++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include "LayerTransactionTest.h" #include "utils/TransactionUtils.h" diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp index 069f199cab..c62f493941 100644 --- a/services/surfaceflinger/tests/ScreenCapture_test.cpp +++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp @@ -20,7 +20,7 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" -#include +#include #include #include diff --git a/services/surfaceflinger/tests/TextureFiltering_test.cpp b/services/surfaceflinger/tests/TextureFiltering_test.cpp index f8c536430a..3f39cf6eea 100644 --- a/services/surfaceflinger/tests/TextureFiltering_test.cpp +++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp index db6df229d5..4bc134fae8 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp @@ -18,7 +18,7 @@ #define LOG_TAG "LibSurfaceFlingerUnittests" #include -#include +#include #include #include diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h index efce6b6b44..0bedcd174e 100644 --- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h +++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h @@ -15,7 +15,7 @@ */ #pragma once -#include +#include #include #include #include -- cgit v1.2.3-59-g8ed1b From a3da302041614624faefd7a4ec8d156dae418043 Mon Sep 17 00:00:00 2001 From: Jim Shargo Date: Wed, 10 Jul 2024 23:39:10 +0000 Subject: Surface: Add a death notification to SurfaceListener As we move away from IGBPs being available generally, clients may still want to know if the other end of the Surface dies on them. With no binder object available to them, if the client requests it, we wrap the callback in a proxy and notify via SurfaceListener::onRemoteDied when they request it via SurfaceListener::needsDeathNotify. See go/warren-buffers for more details. BYPASS_IGBP_IGBC_API_REASON=warren buffers Bug: 340933794 Flag: com.android.graphics.libgui.flags.wb_platform_api_improvements Test: new tests in libgui_test Change-Id: I102c21436ba3d852481bfa636c5de102ef244e4a --- libs/gui/Surface.cpp | 41 +++++++++++++++++++++++++++++++++++ libs/gui/include/gui/Surface.h | 32 +++++++++++++++++++++++++++ libs/gui/tests/Surface_test.cpp | 48 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+) (limited to 'libs/gui/Surface.cpp') diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index da3886ccf0..76d74fef88 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -77,9 +77,28 @@ bool isInterceptorRegistrationOp(int op) { } // namespace +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +Surface::ProducerDeathListenerProxy::ProducerDeathListenerProxy(wp surfaceListener) + : mSurfaceListener(surfaceListener) {} + +void Surface::ProducerDeathListenerProxy::binderDied(const wp&) { + sp surfaceListener = mSurfaceListener.promote(); + if (!surfaceListener) { + return; + } + + if (surfaceListener->needsDeathNotify()) { + surfaceListener->onRemoteDied(); + } +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + Surface::Surface(const sp& bufferProducer, bool controlledByApp, const sp& surfaceControlHandle) : mGraphicBufferProducer(bufferProducer), +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + mSurfaceDeathListener(nullptr), +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) mCrop(Rect::EMPTY_RECT), mBufferAge(0), mGenerationNumber(0), @@ -134,6 +153,12 @@ Surface::~Surface() { if (mConnectedToCpu) { Surface::disconnect(NATIVE_WINDOW_API_CPU); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + if (mSurfaceDeathListener != nullptr) { + IInterface::asBinder(mGraphicBufferProducer)->unlinkToDeath(mSurfaceDeathListener); + mSurfaceDeathListener = nullptr; + } +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) } sp Surface::composerService() const { @@ -2033,6 +2058,7 @@ int Surface::connect(int api, const sp& listener, bool reportBu Mutex::Autolock lock(mMutex); IGraphicBufferProducer::QueueBufferOutput output; mReportRemovedBuffers = reportBufferRemoval; + if (listener != nullptr) { mListenerProxy = new ProducerListenerProxy(this, listener); } @@ -2053,6 +2079,13 @@ int Surface::connect(int api, const sp& listener, bool reportBu } mConsumerRunningBehind = (output.numPendingBuffers >= 2); + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + if (listener && listener->needsDeathNotify()) { + mSurfaceDeathListener = sp::make(listener); + IInterface::asBinder(mGraphicBufferProducer)->linkToDeath(mSurfaceDeathListener); + } +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) } if (!err && api == NATIVE_WINDOW_API_CPU) { mConnectedToCpu = true; @@ -2093,6 +2126,14 @@ int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) { mConnectedToCpu = false; } } + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + if (mSurfaceDeathListener != nullptr) { + IInterface::asBinder(mGraphicBufferProducer)->unlinkToDeath(mSurfaceDeathListener); + mSurfaceDeathListener = nullptr; + } +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + return err; } diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 0f51f2dc13..5ea81c4c25 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -66,6 +66,16 @@ public: virtual void onBufferAttached() {} virtual bool needsAttachNotify() { return false; } #endif + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + // Called if this Surface is connected to a remote implementation and it + // dies or becomes unavailable. + virtual void onRemoteDied() {} + + // Clients will overwrite this if they want to receive a notification + // via onRemoteDied. This should return a constant value. + virtual bool needsDeathNotify() { return false; } +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) }; class StubSurfaceListener : public SurfaceListener { @@ -471,6 +481,21 @@ protected: sp mSurfaceListener; }; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + class ProducerDeathListenerProxy : public IBinder::DeathRecipient { + public: + ProducerDeathListenerProxy(wp surfaceListener); + ProducerDeathListenerProxy(ProducerDeathListenerProxy&) = delete; + + // IBinder::DeathRecipient + virtual void binderDied(const wp&) override; + + private: + wp mSurfaceListener; + }; + friend class ProducerDeathListenerProxy; +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + void querySupportedTimestampsLocked() const; void freeAllBuffers(); @@ -502,6 +527,13 @@ protected: // TODO: rename to mBufferProducer sp mGraphicBufferProducer; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + // mSurfaceDeathListener gets registered as mGraphicBufferProducer's + // DeathRecipient when SurfaceListener::needsDeathNotify returns true and + // gets notified when it dies. + sp mSurfaceDeathListener; +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + // mSlots stores the buffers that have been allocated for each buffer slot. // It is initialized to null pointers, and gets filled in with the result of // IGraphicBufferProducer::requestBuffer when the client dequeues a buffer from a diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index ab09dfc58d..0c3859ebe0 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -50,7 +50,10 @@ #include #include +#include #include +#include +#include #include #include @@ -108,6 +111,18 @@ private: std::vector> mDiscardedBuffers; }; +class DeathWatcherListener : public StubSurfaceListener { +public: + virtual void onRemoteDied() { mDiedPromise.set_value(true); } + + virtual bool needsDeathNotify() { return true; } + + std::future getDiedFuture() { return mDiedPromise.get_future(); } + +private: + std::promise mDiedPromise; +}; + class SurfaceTest : public ::testing::Test { protected: SurfaceTest() { @@ -2374,6 +2389,39 @@ TEST_F(SurfaceTest, ViewSurface_toString) { surface.name = String16("name"); EXPECT_EQ("name", surface.toString()); } + +TEST_F(SurfaceTest, TestRemoteSurfaceDied_CallbackCalled) { + sp testServer = TestServerClient::Create(); + sp producer = testServer->CreateProducer(); + EXPECT_NE(nullptr, producer); + + sp surface = sp::make(producer); + sp deathWatcher = sp::make(); + EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, deathWatcher)); + + auto diedFuture = deathWatcher->getDiedFuture(); + EXPECT_EQ(OK, testServer->Kill()); + + diedFuture.wait(); + EXPECT_TRUE(diedFuture.get()); +} + +TEST_F(SurfaceTest, TestRemoteSurfaceDied_Disconnect_CallbackNotCalled) { + sp testServer = TestServerClient::Create(); + sp producer = testServer->CreateProducer(); + EXPECT_NE(nullptr, producer); + + sp surface = sp::make(producer); + sp deathWatcher = sp::make(); + EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, deathWatcher)); + EXPECT_EQ(OK, surface->disconnect(NATIVE_WINDOW_API_CPU)); + + auto watcherDiedFuture = deathWatcher->getDiedFuture(); + EXPECT_EQ(OK, testServer->Kill()); + + std::future_status status = watcherDiedFuture.wait_for(std::chrono::seconds(1)); + EXPECT_EQ(std::future_status::timeout, status); +} #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) } // namespace android -- cgit v1.2.3-59-g8ed1b From bad6c068828aa9471a8a93924474964c619f465a Mon Sep 17 00:00:00 2001 From: Jim Shargo Date: Wed, 10 Jul 2024 23:39:10 +0000 Subject: Surface: Add a SurfaceQueueOutput struct for replaced buffers With IGBPs, this is provided back to clients via an output object, which Surface doesn't currently provide. This lets us notify clients when this happens. BYPASS_IGBP_IGBC_API_REASON=warren buffers Bug: 340933794 Flag: com.android.graphics.libgui.flags.wb_platform_api_improvements Test: new SurfaceTest tests Change-Id: I2ec5a0598988fdfcfe7356a14be0f99a206ef6a8 --- libs/gui/Surface.cpp | 42 ++++++++++++++---- libs/gui/include/gui/Surface.h | 22 +++++++++- libs/gui/tests/Surface_test.cpp | 95 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 10 deletions(-) (limited to 'libs/gui/Surface.cpp') diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 76d74fef88..66e7ddd915 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -741,11 +741,12 @@ status_t Surface::dequeueBuffer(sp* buffer, sp* outFence) return res; } -status_t Surface::queueBuffer(const sp& buffer, const sp& fd) { +status_t Surface::queueBuffer(const sp& buffer, const sp& fd, + SurfaceQueueBufferOutput* output) { if (buffer == nullptr) { return BAD_VALUE; } - return queueBuffer(buffer.get(), fd ? fd->get() : -1); + return queueBuffer(buffer.get(), fd ? fd->get() : -1, output); } status_t Surface::detachBuffer(const sp& buffer) { @@ -1195,7 +1196,8 @@ void Surface::onBufferQueuedLocked(int slot, sp fence, #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) -int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { +int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd, + SurfaceQueueBufferOutput* surfaceOutput) { ATRACE_CALL(); ALOGV("Surface::queueBuffer"); @@ -1245,16 +1247,26 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { onBufferQueuedLocked(slot, fence, output); } + if (surfaceOutput != nullptr) { + *surfaceOutput = {.bufferReplaced = output.bufferReplaced}; + } + return err; } -int Surface::queueBuffers(const std::vector& buffers) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +int Surface::queueBuffers(const std::vector& buffers, + std::vector* queueBufferOutputs) +#else +int Surface::queueBuffers(const std::vector& buffers) +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +{ ATRACE_CALL(); ALOGV("Surface::queueBuffers"); size_t numBuffers = buffers.size(); - std::vector queueBufferInputs(numBuffers); - std::vector queueBufferOutputs; + std::vector igbpQueueBufferInputs(numBuffers); + std::vector igbpQueueBufferOutputs; std::vector bufferSlots(numBuffers, -1); std::vector> bufferFences(numBuffers); @@ -1280,12 +1292,13 @@ int Surface::queueBuffers(const std::vector& buffers) { IGraphicBufferProducer::QueueBufferInput input; getQueueBufferInputLocked(buffers[batchIdx].buffer, buffers[batchIdx].fenceFd, buffers[batchIdx].timestamp, &input); + input.slot = i; bufferFences[batchIdx] = input.fence; - queueBufferInputs[batchIdx] = input; + igbpQueueBufferInputs[batchIdx] = input; } } nsecs_t now = systemTime(); - err = mGraphicBufferProducer->queueBuffers(queueBufferInputs, &queueBufferOutputs); + err = mGraphicBufferProducer->queueBuffers(igbpQueueBufferInputs, &igbpQueueBufferOutputs); { Mutex::Autolock lock(mMutex); mLastQueueDuration = systemTime() - now; @@ -1295,9 +1308,20 @@ int Surface::queueBuffers(const std::vector& buffers) { for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) { onBufferQueuedLocked(bufferSlots[batchIdx], bufferFences[batchIdx], - queueBufferOutputs[batchIdx]); + igbpQueueBufferOutputs[batchIdx]); + } + } + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + if (queueBufferOutputs != nullptr) { + queueBufferOutputs->clear(); + queueBufferOutputs->resize(numBuffers); + for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) { + (*queueBufferOutputs)[batchIdx].bufferReplaced = + igbpQueueBufferOutputs[batchIdx].bufferReplaced; } } +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) return err; } diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 5ea81c4c25..e74f9ad1dc 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -87,6 +87,15 @@ public: virtual void onBufferDetached(int /*slot*/) override {} }; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +// Contains additional data from the queueBuffer operation. +struct SurfaceQueueBufferOutput { + // True if this queueBuffer caused a buffer to be replaced in the queue + // (and therefore not will not be acquired) + bool bufferReplaced = false; +}; +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + /* * An implementation of ANativeWindow that feeds graphics buffers into a * BufferQueue. @@ -363,7 +372,12 @@ private: protected: virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd); virtual int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd, + SurfaceQueueBufferOutput* surfaceOutput = nullptr); +#else virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) virtual int perform(int operation, va_list args); virtual int setSwapInterval(int interval); @@ -422,7 +436,8 @@ public: // Queues a buffer, with an optional fd fence that captures pending work on the buffer. This // buffer must have been returned by dequeueBuffer or associated with this Surface via an // attachBuffer operation. - status_t queueBuffer(const sp& buffer, const sp& fd = Fence::NO_FENCE); + status_t queueBuffer(const sp& buffer, const sp& fd = Fence::NO_FENCE, + SurfaceQueueBufferOutput* output = nullptr); // Detaches this buffer, dissociating it from this Surface. This buffer must have been returned // by queueBuffer or associated with this Surface via an attachBuffer operation. @@ -443,8 +458,13 @@ public: int fenceFd = -1; nsecs_t timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; }; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + virtual int queueBuffers(const std::vector& buffers, + std::vector* queueBufferOutputs = nullptr); +#else virtual int queueBuffers( const std::vector& buffers); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) protected: enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS }; diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 0c3859ebe0..88893b64ba 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -2422,6 +2422,101 @@ TEST_F(SurfaceTest, TestRemoteSurfaceDied_Disconnect_CallbackNotCalled) { std::future_status status = watcherDiedFuture.wait_for(std::chrono::seconds(1)); EXPECT_EQ(std::future_status::timeout, status); } + +TEST_F(SurfaceTest, QueueBufferOutput_TracksReplacements) { + sp consumer = sp::make(GRALLOC_USAGE_SW_READ_OFTEN); + ASSERT_EQ(OK, consumer->setMaxBufferCount(3)); + ASSERT_EQ(OK, consumer->setMaxAcquiredBufferCount(1)); + + sp surface = consumer->getSurface(); + sp listener = sp::make(); + + // Async mode sets up an extra buffer so the surface can queue it without waiting. + ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(1)); + ASSERT_EQ(OK, surface->setAsyncMode(true)); + ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener)); + + sp buffer; + sp fence; + SurfaceQueueBufferOutput output; + BufferItem item; + + // We can queue directly, without an output arg. + EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)); + EXPECT_EQ(OK, surface->queueBuffer(buffer, fence)); + EXPECT_EQ(OK, consumer->acquireBuffer(&item, 0)); + EXPECT_EQ(OK, consumer->releaseBuffer(item)); + + // We can queue with an output arg, and that we don't expect to see a replacement. + EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)); + EXPECT_EQ(OK, surface->queueBuffer(buffer, fence, &output)); + EXPECT_FALSE(output.bufferReplaced); + + // We expect see a replacement when we queue a second buffer in async mode, and the consumer + // hasn't acquired the first one yet. + EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)); + EXPECT_EQ(OK, surface->queueBuffer(buffer, fence, &output)); + EXPECT_TRUE(output.bufferReplaced); +} + +TEST_F(SurfaceTest, QueueBufferOutput_TracksReplacements_Plural) { + sp consumer = sp::make(GRALLOC_USAGE_SW_READ_OFTEN); + ASSERT_EQ(OK, consumer->setMaxBufferCount(4)); + ASSERT_EQ(OK, consumer->setMaxAcquiredBufferCount(1)); + + sp surface = consumer->getSurface(); + consumer->setName(String8("TRPTest")); + sp listener = sp::make(); + + // Async mode sets up an extra buffer so the surface can queue it without waiting. + ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(2)); + ASSERT_EQ(OK, surface->setAsyncMode(true)); + ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener)); + + // dequeueBuffers requires a vector of a certain size: + std::vector buffers(2); + std::vector queuedBuffers; + std::vector outputs; + BufferItem item; + + auto moveBuffersToQueuedBuffers = [&]() { + EXPECT_EQ(2u, buffers.size()); + EXPECT_NE(nullptr, buffers[0].buffer); + EXPECT_NE(nullptr, buffers[1].buffer); + + queuedBuffers.clear(); + for (auto& buffer : buffers) { + auto& queuedBuffer = queuedBuffers.emplace_back(); + queuedBuffer.buffer = buffer.buffer; + queuedBuffer.fenceFd = buffer.fenceFd; + queuedBuffer.timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; + } + buffers = {{}, {}}; + }; + + // We can queue directly, without an output arg. + EXPECT_EQ(OK, surface->dequeueBuffers(&buffers)); + moveBuffersToQueuedBuffers(); + EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers)); + EXPECT_EQ(OK, consumer->acquireBuffer(&item, 0)); + EXPECT_EQ(OK, consumer->releaseBuffer(item)); + + // We can queue with an output arg. Only the second one should be replaced. + EXPECT_EQ(OK, surface->dequeueBuffers(&buffers)); + moveBuffersToQueuedBuffers(); + EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers, &outputs)); + EXPECT_EQ(2u, outputs.size()); + EXPECT_FALSE(outputs[0].bufferReplaced); + EXPECT_TRUE(outputs[1].bufferReplaced); + + // Since we haven't acquired anything, both queued buffers will replace the original one. + EXPECT_EQ(OK, surface->dequeueBuffers(&buffers)); + moveBuffersToQueuedBuffers(); + EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers, &outputs)); + EXPECT_EQ(2u, outputs.size()); + EXPECT_TRUE(outputs[0].bufferReplaced); + EXPECT_TRUE(outputs[1].bufferReplaced); +} #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) } // namespace android -- cgit v1.2.3-59-g8ed1b