Snap for 6069307 from 86ced3ef2e7f50c200ad483009eb2a214029f012 to qt-qpr2-release

Change-Id: I26fb844d1e7c752fc67942392201e431054f20f6
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 528bfb1..3a7cb44 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -166,7 +166,9 @@
                         mCore->mFreeBuffers.push_back(front->mSlot);
                     }
 
-                    listener = mCore->mConnectedProducerListener;
+                    if (mCore->mBufferReleasedCbEnabled) {
+                        listener = mCore->mConnectedProducerListener;
+                    }
                     ++numDroppedBuffers;
                 }
 
@@ -457,7 +459,9 @@
             mCore->mFreeBuffers.push_back(slot);
         }
 
-        listener = mCore->mConnectedProducerListener;
+        if (mCore->mBufferReleasedCbEnabled) {
+            listener = mCore->mConnectedProducerListener;
+        }
         BQ_LOGV("releaseBuffer: releasing slot %d", slot);
 
         mCore->mDequeueCondition.notify_all();
@@ -668,7 +672,7 @@
         BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers);
         mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
         VALIDATE_CONSISTENCY();
-        if (delta < 0) {
+        if (delta < 0 && mCore->mBufferReleasedCbEnabled) {
             listener = mCore->mConsumerListener;
         }
     }
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index e0e3431..0264bd2 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -65,6 +65,7 @@
     mConnectedApi(NO_CONNECTED_API),
     mLinkedToDeath(),
     mConnectedProducerListener(),
+    mBufferReleasedCbEnabled(false),
     mSlots(),
     mQueue(),
     mFreeSlots(),
@@ -260,6 +261,12 @@
 }
 
 void BufferQueueCore::discardFreeBuffersLocked() {
+    // Notify producer about the discarded buffers.
+    if (mConnectedProducerListener != nullptr && mFreeBuffers.size() > 0) {
+        std::vector<int32_t> freeBuffers(mFreeBuffers.begin(), mFreeBuffers.end());
+        mConnectedProducerListener->onBuffersDiscarded(freeBuffers);
+    }
+
     for (int s : mFreeBuffers) {
         mFreeSlots.insert(s);
         clearBufferSlotLocked(s);
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 92ab410..a317aaf 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -1221,9 +1221,8 @@
                     }
                     mCore->mLinkedToDeath = listener;
                 }
-                if (listener->needsReleaseNotify()) {
-                    mCore->mConnectedProducerListener = listener;
-                }
+                mCore->mConnectedProducerListener = listener;
+                mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify();
             }
             break;
         default:
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index 936063a..808e336 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -24,6 +24,7 @@
 enum {
     ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION,
     NEEDS_RELEASE_NOTIFY,
+    ON_BUFFERS_DISCARDED,
 };
 
 class BpProducerListener : public BpInterface<IProducerListener>
@@ -56,6 +57,13 @@
         }
         return result;
     }
+
+    virtual void onBuffersDiscarded(const std::vector<int>& discardedSlots) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
+        data.writeInt32Vector(discardedSlots);
+        remote()->transact(ON_BUFFERS_DISCARDED, data, &reply, IBinder::FLAG_ONEWAY);
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -76,6 +84,10 @@
     virtual bool needsReleaseNotify() override {
         return mBase->needsReleaseNotify();
     }
+
+    virtual void onBuffersDiscarded(const std::vector<int32_t>& discardedSlots) override {
+        return mBase->onBuffersDiscarded(discardedSlots);
+    }
 };
 
 IMPLEMENT_HYBRID_META_INTERFACE(ProducerListener,
@@ -92,6 +104,17 @@
             CHECK_INTERFACE(IProducerListener, data, reply);
             reply->writeBool(needsReleaseNotify());
             return NO_ERROR;
+        case ON_BUFFERS_DISCARDED: {
+            CHECK_INTERFACE(IProducerListener, data, reply);
+            std::vector<int32_t> discardedSlots;
+            status_t result = data.readInt32Vector(&discardedSlots);
+            if (result != NO_ERROR) {
+                ALOGE("ON_BUFFERS_DISCARDED failed to read discardedSlots: %d", result);
+                return result;
+            }
+            onBuffersDiscarded(discardedSlots);
+            return NO_ERROR;
+        }
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
@@ -104,4 +127,7 @@
     return true;
 }
 
+void BnProducerListener::onBuffersDiscarded(const std::vector<int32_t>& /*discardedSlots*/) {
+}
+
 } // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 9fe5de8..b822319 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -35,6 +35,7 @@
 
 #include <ui/DisplayStatInfo.h>
 #include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
 #include <ui/HdrCapabilities.h>
 #include <ui/Region.h>
 
@@ -1287,6 +1288,14 @@
 }
 
 int Surface::connect(
+        int api, bool reportBufferRemoval, const sp<SurfaceListener>& sListener) {
+    if (sListener != nullptr) {
+        mListenerProxy = new ProducerListenerProxy(this, sListener);
+    }
+    return connect(api, mListenerProxy, reportBufferRemoval);
+}
+
+int Surface::connect(
         int api, const sp<IProducerListener>& listener, bool reportBufferRemoval) {
     ATRACE_CALL();
     ALOGV("Surface::connect");
@@ -1684,6 +1693,28 @@
     }
 }
 
+status_t Surface::getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots,
+        std::vector<sp<GraphicBuffer>>* outBuffers) {
+    ALOGV("Surface::getAndFlushBuffersFromSlots");
+    for (int32_t i : slots) {
+        if (i < 0 || i >= NUM_BUFFER_SLOTS) {
+            ALOGE("%s: Invalid slotIndex: %d", __FUNCTION__, i);
+            return BAD_VALUE;
+        }
+    }
+
+    Mutex::Autolock lock(mMutex);
+    for (int32_t i : slots) {
+        if (mSlots[i].buffer == nullptr) {
+            ALOGW("%s: Discarded slot %d doesn't contain buffer!", __FUNCTION__, i);
+            continue;
+        }
+        outBuffers->push_back(mSlots[i].buffer);
+        mSlots[i].buffer = nullptr;
+    }
+    return OK;
+}
+
 void Surface::setSurfaceDamage(android_native_rect_t* rects, size_t numRects) {
     ATRACE_CALL();
     ALOGV("Surface::setSurfaceDamage");
@@ -1951,4 +1982,22 @@
     return err;
 }
 
+void Surface::ProducerListenerProxy::onBuffersDiscarded(const std::vector<int32_t>& slots) {
+    ATRACE_CALL();
+    sp<Surface> parent = mParent.promote();
+    if (parent == nullptr) {
+        return;
+    }
+
+    std::vector<sp<GraphicBuffer>> discardedBufs;
+    status_t res = parent->getAndFlushBuffersFromSlots(slots, &discardedBufs);
+    if (res != OK) {
+        ALOGE("%s: Failed to get buffers from slots: %s(%d)", __FUNCTION__,
+                strerror(-res), res);
+        return;
+    }
+
+    mSurfaceListener->onBuffersDiscarded(discardedBufs);
+}
+
 }; // namespace android
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 690a85f..17617bc 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -189,8 +189,12 @@
     sp<IProducerListener> mLinkedToDeath;
 
     // mConnectedProducerListener is used to handle the onBufferReleased
-    // notification.
+    // and onBuffersDiscarded notification.
     sp<IProducerListener> mConnectedProducerListener;
+    // mBufferReleasedCbEnabled is used to indicate whether onBufferReleased()
+    // callback is registered by the listener. When set to false,
+    // mConnectedProducerListener will not trigger onBufferReleased() callback.
+    bool mBufferReleasedCbEnabled;
 
     // mSlots is an array of buffer slots that must be mirrored on the producer
     // side. This allows buffer ownership to be transferred between the producer
diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h
index a13d8e4..32a3690 100644
--- a/libs/gui/include/gui/IProducerListener.h
+++ b/libs/gui/include/gui/IProducerListener.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_GUI_IPRODUCERLISTENER_H
 #define ANDROID_GUI_IPRODUCERLISTENER_H
 
+#include <vector>
+
 #include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
 #include <android/hardware/graphics/bufferqueue/2.0/IProducerListener.h>
 #include <binder/IInterface.h>
@@ -44,6 +46,9 @@
     // multiple threads.
     virtual void onBufferReleased() = 0; // Asynchronous
     virtual bool needsReleaseNotify() = 0;
+    // onBuffersFreed is called from IGraphicBufferConsumer::discardFreeBuffers
+    // to notify the producer that certain free buffers are discarded by the consumer.
+    virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) = 0; // Asynchronous
 };
 
 class IProducerListener : public ProducerListener, public IInterface
@@ -65,6 +70,7 @@
     virtual status_t onTransact(uint32_t code, const Parcel& data,
             Parcel* reply, uint32_t flags = 0);
     virtual bool needsReleaseNotify();
+    virtual void onBuffersDiscarded(const std::vector<int32_t>& slots);
 };
 
 class DummyProducerListener : public BnProducerListener
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 5c6a1ee..a5641b0 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -20,6 +20,7 @@
 #include <gui/BufferQueueDefs.h>
 #include <gui/HdrMetadata.h>
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
 
 #include <ui/ANativeObjectBase.h>
 #include <ui/GraphicTypes.h>
@@ -35,6 +36,21 @@
 
 class ISurfaceComposer;
 
+/* This is the same as ProducerListener except that onBuffersDiscarded is
+ * called with a vector of graphic buffers instead of buffer slots.
+ */
+class SurfaceListener : public virtual RefBase
+{
+public:
+    SurfaceListener() = default;
+    virtual ~SurfaceListener() = default;
+
+    virtual void onBufferReleased() = 0;
+    virtual bool needsReleaseNotify() = 0;
+
+    virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers) = 0;
+};
+
 /*
  * An implementation of ANativeWindow that feeds graphics buffers into a
  * BufferQueue.
@@ -283,6 +299,10 @@
             sp<Fence>* outFence);
     virtual int attachBuffer(ANativeWindowBuffer*);
 
+    virtual int connect(
+            int api, bool reportBufferRemoval,
+            const sp<SurfaceListener>& sListener);
+
     // When client connects to Surface with reportBufferRemoval set to true, any buffers removed
     // from this Surface will be collected and returned here. Once this method returns, these
     // buffers will no longer be referenced by this Surface unless they are attached to this
@@ -299,6 +319,26 @@
     enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS };
     enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
 
+    class ProducerListenerProxy : public BnProducerListener {
+    public:
+        ProducerListenerProxy(wp<Surface> parent, sp<SurfaceListener> listener)
+               : mParent(parent), mSurfaceListener(listener) {}
+        virtual ~ProducerListenerProxy() {}
+
+        virtual void onBufferReleased() {
+            mSurfaceListener->onBufferReleased();
+        }
+
+        virtual bool needsReleaseNotify() {
+            return mSurfaceListener->needsReleaseNotify();
+        }
+
+        virtual void onBuffersDiscarded(const std::vector<int32_t>& slots);
+    private:
+        wp<Surface> mParent;
+        sp<SurfaceListener> mSurfaceListener;
+    };
+
     void querySupportedTimestampsLocked() const;
 
     void freeAllBuffers();
@@ -466,6 +506,10 @@
 
     bool mReportRemovedBuffers = false;
     std::vector<sp<GraphicBuffer>> mRemovedBuffers;
+
+    sp<IProducerListener> mListenerProxy;
+    status_t getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots,
+            std::vector<sp<GraphicBuffer>>* outBuffers);
 };
 
 } // namespace android
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 119e888..406f21f 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -998,12 +998,31 @@
     ASSERT_EQ(true, thirdSegment.usedThirdBuffer);
 }
 
+struct BufferDiscardedListener : public BnProducerListener {
+public:
+    BufferDiscardedListener() = default;
+    virtual ~BufferDiscardedListener() = default;
+
+    virtual void onBufferReleased() {}
+    virtual bool needsReleaseNotify() { return false; }
+    virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) {
+        mDiscardedSlots.insert(mDiscardedSlots.end(), slots.begin(), slots.end());
+    }
+
+    const std::vector<int32_t>& getDiscardedSlots() const { return mDiscardedSlots; }
+private:
+    // No need to use lock given the test triggers the listener in the same
+    // thread context.
+    std::vector<int32_t> mDiscardedSlots;
+};
+
 TEST_F(BufferQueueTest, TestDiscardFreeBuffers) {
     createBufferQueue();
     sp<DummyConsumer> dc(new DummyConsumer);
     ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
     IGraphicBufferProducer::QueueBufferOutput output;
-    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+    sp<BufferDiscardedListener> pl(new BufferDiscardedListener);
+    ASSERT_EQ(OK, mProducer->connect(pl,
             NATIVE_WINDOW_API_CPU, false, &output));
 
     int slot = BufferQueue::INVALID_BUFFER_SLOT;
@@ -1044,12 +1063,19 @@
     ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
     ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
                     EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+    int releasedSlot = item.mSlot;
+
     // Acquire 1 buffer, leaving 1 filled buffer in queue
     ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
 
     // Now discard the free buffers
     ASSERT_EQ(OK, mConsumer->discardFreeBuffers());
 
+    // Check onBuffersDiscarded is called with correct slots
+    auto buffersDiscarded = pl->getDiscardedSlots();
+    ASSERT_EQ(buffersDiscarded.size(), 1);
+    ASSERT_EQ(buffersDiscarded[0], releasedSlot);
+
     // Check no free buffers in dump
     String8 dumpString;
     mConsumer->dumpState(String8{}, &dumpString);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index d370858..a851687 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -57,6 +57,37 @@
 
 static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max();
 
+class DummySurfaceListener : public SurfaceListener {
+public:
+    DummySurfaceListener(bool enableReleasedCb = false) :
+            mEnableReleaseCb(enableReleasedCb),
+            mBuffersReleased(0) {}
+    virtual ~DummySurfaceListener() = default;
+
+    virtual void onBufferReleased() {
+        mBuffersReleased++;
+    }
+    virtual bool needsReleaseNotify() {
+        return mEnableReleaseCb;
+    }
+    virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers) {
+        mDiscardedBuffers.insert(mDiscardedBuffers.end(), buffers.begin(), buffers.end());
+    }
+
+    int getReleaseNotifyCount() const {
+        return mBuffersReleased;
+    }
+    const std::vector<sp<GraphicBuffer>>& getDiscardedBuffers() const {
+        return mDiscardedBuffers;
+    }
+private:
+    // No need to use lock given the test triggers the listener in the same
+    // thread context.
+    bool mEnableReleaseCb;
+    int32_t mBuffersReleased;
+    std::vector<sp<GraphicBuffer>> mDiscardedBuffers;
+};
+
 class SurfaceTest : public ::testing::Test {
 protected:
     SurfaceTest() {
@@ -88,6 +119,86 @@
         mComposerClient->dispose();
     }
 
+    void testSurfaceListener(bool hasSurfaceListener, bool enableReleasedCb,
+            int32_t extraDiscardedBuffers) {
+        sp<IGraphicBufferProducer> producer;
+        sp<IGraphicBufferConsumer> consumer;
+        BufferQueue::createBufferQueue(&producer, &consumer);
+
+        sp<DummyConsumer> dummyConsumer(new DummyConsumer);
+        consumer->consumerConnect(dummyConsumer, false);
+        consumer->setConsumerName(String8("TestConsumer"));
+
+        sp<Surface> surface = new Surface(producer);
+        sp<ANativeWindow> window(surface);
+        sp<DummySurfaceListener> listener;
+        if (hasSurfaceListener) {
+            listener = new DummySurfaceListener(enableReleasedCb);
+        }
+        ASSERT_EQ(OK, surface->connect(
+                NATIVE_WINDOW_API_CPU,
+                /*reportBufferRemoval*/true,
+                /*listener*/listener));
+        const int BUFFER_COUNT = 4 + extraDiscardedBuffers;
+        ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
+
+        ANativeWindowBuffer* buffers[BUFFER_COUNT];
+        // Dequeue first to allocate a number of buffers
+        for (int i = 0; i < BUFFER_COUNT; i++) {
+            ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(window.get(), &buffers[i]));
+        }
+        for (int i = 0; i < BUFFER_COUNT; i++) {
+            ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], -1));
+        }
+
+        ANativeWindowBuffer* buffer;
+        // Fill BUFFER_COUNT-1 buffers
+        for (int i = 0; i < BUFFER_COUNT-1; i++) {
+            ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(window.get(), &buffer));
+            ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, -1));
+        }
+
+        // Dequeue 1 buffer
+        ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(window.get(), &buffer));
+
+        // Acquire and free 1+extraDiscardedBuffers buffer, check onBufferReleased is called.
+        std::vector<BufferItem> releasedItems;
+        releasedItems.resize(1+extraDiscardedBuffers);
+        for (int i = 0; i < releasedItems.size(); i++) {
+            ASSERT_EQ(NO_ERROR, consumer->acquireBuffer(&releasedItems[i], 0));
+            ASSERT_EQ(NO_ERROR, consumer->releaseBuffer(releasedItems[i].mSlot,
+                    releasedItems[i].mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR,
+                    Fence::NO_FENCE));
+        }
+        int32_t expectedReleaseCb = (enableReleasedCb ? releasedItems.size() : 0);
+        if (hasSurfaceListener) {
+            ASSERT_EQ(expectedReleaseCb, listener->getReleaseNotifyCount());
+        }
+
+        // Acquire 1 buffer, leaving 1+extraDiscardedBuffers filled buffer in queue
+        BufferItem item;
+        ASSERT_EQ(NO_ERROR, consumer->acquireBuffer(&item, 0));
+
+        // Discard free buffers
+        ASSERT_EQ(NO_ERROR, consumer->discardFreeBuffers());
+
+        if (hasSurfaceListener) {
+            ASSERT_EQ(expectedReleaseCb, listener->getReleaseNotifyCount());
+
+            // Check onBufferDiscarded is called with correct buffer
+            auto discardedBuffers = listener->getDiscardedBuffers();
+            ASSERT_EQ(discardedBuffers.size(), releasedItems.size());
+            for (int i = 0; i < releasedItems.size(); i++) {
+                ASSERT_EQ(discardedBuffers[i], releasedItems[i].mGraphicBuffer);
+            }
+
+            ASSERT_EQ(expectedReleaseCb, listener->getReleaseNotifyCount());
+        }
+
+        // Disconnect the surface
+        ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
+    }
+
     sp<Surface> mSurface;
     sp<SurfaceComposerClient> mComposerClient;
     sp<SurfaceControl> mSurfaceControl;
@@ -480,6 +591,21 @@
     ASSERT_LE(removedBuffers.size(), 1u);
 }
 
+TEST_F(SurfaceTest, SurfaceListenerTest) {
+    // Test discarding 1 free buffers with no listener
+    testSurfaceListener(/*hasListener*/false, /*enableReleaseCb*/false, /*extraDiscardedBuffers*/0);
+    // Test discarding 2 free buffers with no listener
+    testSurfaceListener(/*hasListener*/false, /*enableReleaseCb*/false, /*extraDiscardedBuffers*/1);
+    // Test discarding 1 free buffers with a listener, disabling onBufferReleased
+    testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/false, /*extraDiscardedBuffers*/0);
+    // Test discarding 2 free buffers with a listener, disabling onBufferReleased
+    testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/false, /*extraDiscardedBuffers*/1);
+    // Test discarding 1 free buffers with a listener, enabling onBufferReleased
+    testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/true, /*extraDiscardedBuffers*/0);
+    // Test discarding 3 free buffers with a listener, enabling onBufferReleased
+    testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/true, /*extraDiscardedBuffers*/2);
+}
+
 TEST_F(SurfaceTest, TestGetLastDequeueStartTime) {
     sp<ANativeWindow> anw(mSurface);
     ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU));