Add interface for controlling single buffer auto refresh

- Adds a boolean to BufferQueue that controls whether or not auto
  refresh is enabled in SurfaceFlinger when in single buffer mode.
- Adds plumbing up to ANativeWindow.
- When enabled, it will cache the shared buffer slot in Surface in
  order to prevent the Binder transaction with SurfaceFlinger.

Bug 24940410

Change-Id: I83142afdc00e203f198a32288f071d926f8fda95
diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h
index a515f39..6f45181 100644
--- a/include/gui/BufferItem.h
+++ b/include/gui/BufferItem.h
@@ -119,8 +119,10 @@
     // previous frame
     Region mSurfaceDamage;
 
-    // Indicates that the BufferQueue is in single buffer mode
-    bool mSingleBufferMode;
+    // Indicates that the consumer should acquire the next frame as soon as it
+    // can and not wait for a frame to become available. This is only relevant
+    // in single buffer mode.
+    bool mAutoRefresh;
 
     // Indicates that this buffer was queued by the producer. When in single
     // buffer mode acquire() can return a BufferItem that wasn't in the queue.
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index e2e73a0..d10ba2f 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -292,6 +292,11 @@
     // consumer and producer to access the same buffer simultaneously.
     bool mSingleBufferMode;
 
+    // When single buffer mode is enabled, this indicates whether the consumer
+    // should acquire buffers even if BufferQueue doesn't indicate that they are
+    // available.
+    bool mAutoRefresh;
+
     // When single buffer mode is enabled, this tracks which slot contains the
     // shared buffer.
     int mSingleBufferSlot;
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index dc05e98..312f323 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -176,6 +176,9 @@
     // See IGraphicBufferProducer::setSingleBufferMode
     virtual status_t setSingleBufferMode(bool singleBufferMode) override;
 
+    // See IGraphicBufferProducer::setAutoRefresh
+    virtual status_t setAutoRefresh(bool autoRefresh) override;
+
     // See IGraphicBufferProducer::setDequeueTimeout
     virtual status_t setDequeueTimeout(nsecs_t timeout) override;
 
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 265728f..f6b4230 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -531,6 +531,14 @@
     // the producer and consumer to simultaneously access the same buffer.
     virtual status_t setSingleBufferMode(bool singleBufferMode) = 0;
 
+    // Used to enable/disable auto-refresh.
+    //
+    // Auto refresh has no effect outside of single buffer mode. In single
+    // buffer mode, when enabled, it indicates to the consumer that it should
+    // attempt to acquire buffers even if it is not aware of any being
+    // available.
+    virtual status_t setAutoRefresh(bool autoRefresh) = 0;
+
     // Sets how long dequeueBuffer will wait for a buffer to become available
     // before returning an error (TIMED_OUT).
     //
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index f79210b..3afdaae 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -168,6 +168,7 @@
     int dispatchSetBuffersDataSpace(va_list args);
     int dispatchSetSurfaceDamage(va_list args);
     int dispatchSetSingleBufferMode(va_list args);
+    int dispatchSetAutoRefresh(va_list args);
 
 protected:
     virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
@@ -197,6 +198,7 @@
     virtual int setMaxDequeuedBufferCount(int maxDequeuedBuffers);
     virtual int setAsyncMode(bool async);
     virtual int setSingleBufferMode(bool singleBufferMode);
+    virtual int setAutoRefresh(bool autoRefresh);
     virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
     virtual int unlockAndPost();
 
@@ -331,6 +333,19 @@
     // Stores the current generation number. See setGenerationNumber and
     // IGraphicBufferProducer::setGenerationNumber for more information.
     uint32_t mGenerationNumber;
+
+    // Caches the values that have been passed to the producer.
+    bool mSingleBufferMode;
+    bool mAutoRefresh;
+
+    // If in single buffer mode and auto refresh is enabled, store the shared
+    // buffer slot and return it for all calls to queue/dequeue without going
+    // over Binder.
+    int mSharedBufferSlot;
+
+    // This is true if the shared buffer has already been queued/canceled. It's
+    // used to prevent a mismatch between the number of queue/dequeue calls.
+    bool mSharedBufferHasBeenQueued;
 };
 
 }; // namespace android
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 036ef1e..5e3924a 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -38,7 +38,7 @@
     mAcquireCalled(false),
     mTransformToDisplayInverse(false),
     mSurfaceDamage(),
-    mSingleBufferMode(false),
+    mAutoRefresh(false),
     mQueuedBuffer(true),
     mIsStale(false) {
 }
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 92285e5..f8b50cc 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -67,7 +67,7 @@
         }
 
         bool sharedBufferAvailable = mCore->mSingleBufferMode &&
-                mCore->mSingleBufferSlot !=
+                mCore->mAutoRefresh && mCore->mSingleBufferSlot !=
                 BufferQueueCore::INVALID_BUFFER_SLOT;
 
         // In asynchronous mode the list is guaranteed to be one buffer deep,
@@ -214,16 +214,15 @@
                     (mCore->mSingleBufferCache.transform &
                     NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
             outBuffer->mSurfaceDamage = Region::INVALID_REGION;
-            outBuffer->mSingleBufferMode = true;
             outBuffer->mQueuedBuffer = false;
             outBuffer->mIsStale = false;
+            outBuffer->mAutoRefresh = mCore->mSingleBufferMode &&
+                    mCore->mAutoRefresh;
         } else {
             slot = front->mSlot;
             *outBuffer = *front;
         }
 
-        outBuffer->mSingleBufferMode = mCore->mSingleBufferMode;
-
         ATRACE_BUFFER_INDEX(slot);
 
         BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }",
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index f785db0..ba07362 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -79,6 +79,7 @@
     mGenerationNumber(0),
     mAsyncMode(false),
     mSingleBufferMode(false),
+    mAutoRefresh(false),
     mSingleBufferSlot(INVALID_BUFFER_SLOT),
     mSingleBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
             HAL_DATASPACE_UNKNOWN)
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 9d42464..e065e61 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -806,8 +806,8 @@
                 mCore->mDequeueBufferCannotBlock ||
                 (mCore->mSingleBufferMode && mCore->mSingleBufferSlot == slot);
         item.mSurfaceDamage = surfaceDamage;
-        item.mSingleBufferMode = mCore->mSingleBufferMode;
         item.mQueuedBuffer = true;
+        item.mAutoRefresh = mCore->mSingleBufferMode && mCore->mAutoRefresh;
 
         mStickyTransform = stickyTransform;
 
@@ -1309,6 +1309,16 @@
     return NO_ERROR;
 }
 
+status_t BufferQueueProducer::setAutoRefresh(bool autoRefresh) {
+    ATRACE_CALL();
+    BQ_LOGV("setAutoRefresh: %d", autoRefresh);
+
+    Mutex::Autolock lock(mCore->mMutex);
+
+    mCore->mAutoRefresh = autoRefresh;
+    return NO_ERROR;
+}
+
 status_t BufferQueueProducer::setDequeueTimeout(nsecs_t timeout) {
     ATRACE_CALL();
     BQ_LOGV("setDequeueTimeout: %" PRId64, timeout);
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index e1abd45..55059dd 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -407,7 +407,7 @@
     }
 
     // Do whatever sync ops we need to do before releasing the old slot.
-    if (!item.mSingleBufferMode || slot != mCurrentTexture) {
+    if (slot != mCurrentTexture) {
         err = syncForReleaseLocked(mEglDisplay);
         if (err != NO_ERROR) {
             // Release the buffer we just acquired.  It's not safe to
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 2478601..c66694d 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -52,6 +52,7 @@
     SET_ASYNC_MODE,
     GET_NEXT_FRAME_NUMBER,
     SET_SINGLE_BUFFER_MODE,
+    SET_AUTO_REFRESH,
     SET_DEQUEUE_TIMEOUT,
 };
 
@@ -355,6 +356,18 @@
         return result;
     }
 
+    virtual status_t setAutoRefresh(bool autoRefresh) {
+        Parcel data, reply;
+        data.writeInterfaceToken(
+                IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeInt32(autoRefresh);
+        status_t result = remote()->transact(SET_AUTO_REFRESH, data, &reply);
+        if (result == NO_ERROR) {
+            result = reply.readInt32();
+        }
+        return result;
+    }
+
     virtual status_t setDequeueTimeout(nsecs_t timeout) {
         Parcel data, reply;
         data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -562,6 +575,13 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
+        case SET_AUTO_REFRESH: {
+            CHECK_INTERFACE(IGraphicBuffer, data, reply);
+            bool autoRefresh = data.readInt32();
+            status_t result = setAutoRefresh(autoRefresh);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
         case SET_DEQUEUE_TIMEOUT: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             nsecs_t timeout = data.readInt64();
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 6fc55c3..42adf90 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -44,7 +44,11 @@
         bool controlledByApp)
     : mGraphicBufferProducer(bufferProducer),
       mCrop(Rect::EMPTY_RECT),
-      mGenerationNumber(0)
+      mGenerationNumber(0),
+      mSingleBufferMode(false),
+      mAutoRefresh(false),
+      mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
+      mSharedBufferHasBeenQueued(false)
 {
     // Initialize the ANativeWindow function pointers.
     ANativeWindow::setSwapInterval  = hook_setSwapInterval;
@@ -232,6 +236,16 @@
 
         reqFormat = mReqFormat;
         reqUsage = mReqUsage;
+
+        if (mSingleBufferMode && mAutoRefresh && mSharedBufferSlot !=
+                BufferItem::INVALID_BUFFER_SLOT) {
+            sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
+            if (gbuf != NULL) {
+                *buffer = gbuf.get();
+                *fenceFd = -1;
+                return OK;
+            }
+        }
     } // Drop the lock so that we can still touch the Surface while blocking in IGBP::dequeueBuffer
 
     int buf = -1;
@@ -279,6 +293,15 @@
     }
 
     *buffer = gbuf.get();
+
+    if (mSingleBufferMode && mAutoRefresh) {
+        mSharedBufferSlot = buf;
+        mSharedBufferHasBeenQueued = false;
+    } else if (mSharedBufferSlot == buf) {
+        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+        mSharedBufferHasBeenQueued = false;
+    }
+
     return OK;
 }
 
@@ -294,8 +317,19 @@
         }
         return i;
     }
+    if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
+        if (fenceFd >= 0) {
+            close(fenceFd);
+        }
+        return OK;
+    }
     sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
     mGraphicBufferProducer->cancelBuffer(i, fence);
+
+    if (mSingleBufferMode && mAutoRefresh && mSharedBufferSlot == i) {
+        mSharedBufferHasBeenQueued = true;
+    }
+
     return OK;
 }
 
@@ -323,6 +357,7 @@
     Mutex::Autolock lock(mMutex);
     int64_t timestamp;
     bool isAutoTimestamp = false;
+
     if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
         timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
         isAutoTimestamp = true;
@@ -338,6 +373,12 @@
         }
         return i;
     }
+    if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
+        if (fenceFd >= 0) {
+            close(fenceFd);
+        }
+        return OK;
+    }
 
 
     // Make sure the crop rectangle is entirely inside the buffer.
@@ -417,6 +458,7 @@
     if (err != OK)  {
         ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
     }
+
     uint32_t numPendingBuffers = 0;
     uint32_t hint = 0;
     output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
@@ -434,6 +476,10 @@
         mDirtyRegion = Region::INVALID_REGION;
     }
 
+    if (mSingleBufferMode && mAutoRefresh && mSharedBufferSlot == i) {
+        mSharedBufferHasBeenQueued = true;
+    }
+
     return err;
 }
 
@@ -557,6 +603,9 @@
     case NATIVE_WINDOW_SET_SINGLE_BUFFER_MODE:
         res = dispatchSetSingleBufferMode(args);
         break;
+    case NATIVE_WINDOW_SET_AUTO_REFRESH:
+        res = dispatchSetAutoRefresh(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -669,8 +718,12 @@
 
 int Surface::dispatchSetSingleBufferMode(va_list args) {
     bool singleBufferMode = va_arg(args, int);
-    setSingleBufferMode(singleBufferMode);
-    return NO_ERROR;
+    return setSingleBufferMode(singleBufferMode);
+}
+
+int Surface::dispatchSetAutoRefresh(va_list args) {
+    bool autoRefresh = va_arg(args, int);
+    return setAutoRefresh(autoRefresh);
 }
 
 int Surface::connect(int api) {
@@ -714,6 +767,8 @@
     ATRACE_CALL();
     ALOGV("Surface::disconnect");
     Mutex::Autolock lock(mMutex);
+    mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+    mSharedBufferHasBeenQueued = false;
     freeAllBuffers();
     int err = mGraphicBufferProducer->disconnect(api);
     if (!err) {
@@ -796,6 +851,9 @@
 {
     ALOGV("Surface::setUsage");
     Mutex::Autolock lock(mMutex);
+    if (reqUsage != mReqUsage) {
+        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+    }
     mReqUsage = reqUsage;
     return OK;
 }
@@ -888,12 +946,29 @@
 
     status_t err = mGraphicBufferProducer->setSingleBufferMode(
             singleBufferMode);
-    ALOGE_IF(err, "IGraphicsBufferProducer::setSingleBufferMode(%d) returned"
+    if (err == NO_ERROR) {
+        mSingleBufferMode = singleBufferMode;
+    }
+    ALOGE_IF(err, "IGraphicBufferProducer::setSingleBufferMode(%d) returned"
             "%s", singleBufferMode, strerror(-err));
 
     return err;
 }
 
+int Surface::setAutoRefresh(bool autoRefresh) {
+    ATRACE_CALL();
+    ALOGV("Surface::setAutoRefresh (%d)", autoRefresh);
+    Mutex::Autolock lock(mMutex);
+
+    status_t err = mGraphicBufferProducer->setAutoRefresh(autoRefresh);
+    if (err == NO_ERROR) {
+        mAutoRefresh = autoRefresh;
+    }
+    ALOGE_IF(err, "IGraphicBufferProducer::setAutoRefresh(%d) returned %s",
+            autoRefresh, strerror(-err));
+    return err;
+}
+
 int Surface::setBuffersDimensions(uint32_t width, uint32_t height)
 {
     ATRACE_CALL();
@@ -903,6 +978,9 @@
         return BAD_VALUE;
 
     Mutex::Autolock lock(mMutex);
+    if (width != mReqWidth || height != mReqHeight) {
+        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+    }
     mReqWidth = width;
     mReqHeight = height;
     return NO_ERROR;
@@ -917,6 +995,9 @@
         return BAD_VALUE;
 
     Mutex::Autolock lock(mMutex);
+    if (width != mUserWidth || height != mUserHeight) {
+        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+    }
     mUserWidth = width;
     mUserHeight = height;
     return NO_ERROR;
@@ -927,6 +1008,9 @@
     ALOGV("Surface::setBuffersFormat");
 
     Mutex::Autolock lock(mMutex);
+    if (format != mReqFormat) {
+        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+    }
     mReqFormat = format;
     return NO_ERROR;
 }
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index f4c47ed..b6af166 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -65,6 +65,27 @@
         BufferQueue::createBufferQueue(&mProducer, &mConsumer);
     }
 
+    void testBufferItem(const IGraphicBufferProducer::QueueBufferInput& input,
+            const BufferItem& item) {
+        int64_t timestamp;
+        bool isAutoTimestamp;
+        android_dataspace dataSpace;
+        Rect crop;
+        int scalingMode;
+        uint32_t transform;
+        sp<Fence> fence;
+
+        input.deflate(&timestamp, &isAutoTimestamp, &dataSpace, &crop,
+                &scalingMode, &transform, &fence, NULL);
+        ASSERT_EQ(timestamp, item.mTimestamp);
+        ASSERT_EQ(isAutoTimestamp, item.mIsAutoTimestamp);
+        ASSERT_EQ(dataSpace, item.mDataSpace);
+        ASSERT_EQ(crop, item.mCrop);
+        ASSERT_EQ(static_cast<uint32_t>(scalingMode), item.mScalingMode);
+        ASSERT_EQ(transform, item.mTransform);
+        ASSERT_EQ(fence, item.mFence);
+    }
+
     sp<IGraphicBufferProducer> mProducer;
     sp<IGraphicBufferConsumer> mConsumer;
 };
@@ -521,7 +542,7 @@
     ASSERT_EQ(OK, mConsumer->attachBuffer(&outSlot, buffer));
 }
 
-TEST_F(BufferQueueTest, TestSingleBufferMode) {
+TEST_F(BufferQueueTest, TestSingleBufferModeWithoutAutoRefresh) {
     createBufferQueue();
     sp<DummyConsumer> dc(new DummyConsumer);
     ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
@@ -545,19 +566,66 @@
             NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
     ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
 
+    // Repeatedly queue and dequeue a buffer from the producer side, it should
+    // always return the same one. And we won't run out of buffers because it's
+    // always the same one and because async mode gets enabled.
+    int slot;
+    for (int i = 0; i < 5; i++) {
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+        ASSERT_EQ(singleSlot, slot);
+        ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
+    }
+
+    // acquire the buffer
+    BufferItem item;
+    ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+    ASSERT_EQ(singleSlot, item.mSlot);
+    testBufferItem(input, item);
+    ASSERT_EQ(true, item.mQueuedBuffer);
+    ASSERT_EQ(false, item.mAutoRefresh);
+
+    ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+            EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+    // attempt to acquire a second time should return no buffer available
+    ASSERT_EQ(IGraphicBufferConsumer::NO_BUFFER_AVAILABLE,
+            mConsumer->acquireBuffer(&item, 0));
+}
+
+TEST_F(BufferQueueTest, TestSingleBufferModeWithAutoRefresh) {
+    createBufferQueue();
+    sp<DummyConsumer> dc(new DummyConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    IGraphicBufferProducer::QueueBufferOutput output;
+    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+            NATIVE_WINDOW_API_CPU, true, &output));
+
+    ASSERT_EQ(OK, mProducer->setSingleBufferMode(true));
+    ASSERT_EQ(OK, mProducer->setAutoRefresh(true));
+
+    // Get a buffer
+    int singleSlot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buffer;
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+            mProducer->dequeueBuffer(&singleSlot, &fence, 0, 0, 0, 0));
+    ASSERT_EQ(OK, mProducer->requestBuffer(singleSlot, &buffer));
+
+    // Queue the buffer
+    IGraphicBufferProducer::QueueBufferInput input(0, false,
+            HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1),
+            NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+    ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
+
     // Repeatedly acquire and release a buffer from the consumer side, it should
     // always return the same one.
     BufferItem item;
     for (int i = 0; i < 5; i++) {
         ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
         ASSERT_EQ(singleSlot, item.mSlot);
-        ASSERT_EQ(0, item.mTimestamp);
-        ASSERT_EQ(false, item.mIsAutoTimestamp);
-        ASSERT_EQ(HAL_DATASPACE_UNKNOWN, item.mDataSpace);
-        ASSERT_EQ(Rect(0, 0, 1, 1), item.mCrop);
-        ASSERT_EQ(NATIVE_WINDOW_SCALING_MODE_FREEZE, item.mScalingMode);
-        ASSERT_EQ(0u, item.mTransform);
-        ASSERT_EQ(Fence::NO_FENCE, item.mFence);
+        testBufferItem(input, item);
+        ASSERT_EQ(i == 0, item.mQueuedBuffer);
+        ASSERT_EQ(true, item.mAutoRefresh);
 
         ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
                 EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
@@ -585,6 +653,8 @@
         ASSERT_EQ(NATIVE_WINDOW_SCALING_MODE_FREEZE, item.mScalingMode);
         ASSERT_EQ(0u, item.mTransform);
         ASSERT_EQ(Fence::NO_FENCE, item.mFence);
+        ASSERT_EQ(i == 0, item.mQueuedBuffer);
+        ASSERT_EQ(true, item.mAutoRefresh);
 
         ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
                 EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index c7e2afb..05700f8 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -1197,6 +1197,7 @@
     //XXX: temporary hack for the EGL hook-up for single buffer mode
     if (attribute == EGL_RENDER_BUFFER && (value == EGL_BACK_BUFFER ||
             value == EGL_SINGLE_BUFFER)) {
+        native_window_set_auto_refresh(s->win.get(), true);
         return (native_window_set_single_buffer_mode(s->win.get(),
                 value == EGL_SINGLE_BUFFER)) ? EGL_TRUE : EGL_FALSE;
     }
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 64c1dd9..1a0d689 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -547,8 +547,14 @@
     return 0;
 }
 
-status_t VirtualDisplaySurface::setSingleBufferMode(bool singleBufferMode) {
-    return mSource[SOURCE_SINK]->setSingleBufferMode(singleBufferMode);
+status_t VirtualDisplaySurface::setSingleBufferMode(bool /*singleBufferMode*/) {
+    ALOGE("setSingleBufferMode not supported on VirtualDisplaySurface");
+    return INVALID_OPERATION;
+}
+
+status_t VirtualDisplaySurface::setAutoRefresh(bool /*autoRefresh*/) {
+    ALOGE("setAutoRefresh not supported on VirtualDisplaySurface");
+    return INVALID_OPERATION;
 }
 
 status_t VirtualDisplaySurface::setDequeueTimeout(nsecs_t /* timeout */) {
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 7f451a9..ede204c 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -121,6 +121,7 @@
     virtual String8 getConsumerName() const override;
     virtual uint64_t getNextFrameNumber() const override;
     virtual status_t setSingleBufferMode(bool singleBufferMode) override;
+    virtual status_t setAutoRefresh(bool autoRefresh) override;
     virtual status_t setDequeueTimeout(nsecs_t timeout) override;
 
     //
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index d39075f..6574898 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -88,7 +88,7 @@
         mQueueItems(),
         mLastFrameNumberReceived(0),
         mUpdateTexImageFailed(false),
-        mSingleBufferMode(false)
+        mAutoRefresh(false)
 {
     mCurrentCrop.makeInvalid();
     mFlinger->getRenderEngine().genTextures(1, &mTextureName);
@@ -1260,7 +1260,7 @@
 // ----------------------------------------------------------------------------
 
 bool Layer::shouldPresentNow(const DispSync& dispSync) const {
-    if (mSidebandStreamChanged || mSingleBufferMode) {
+    if (mSidebandStreamChanged || mAutoRefresh) {
         return true;
     }
 
@@ -1284,7 +1284,7 @@
 
 bool Layer::onPreComposition() {
     mRefreshPending = false;
-    return mQueuedFrames > 0 || mSidebandStreamChanged || mSingleBufferMode;
+    return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh;
 }
 
 void Layer::onPostComposition() {
@@ -1341,7 +1341,7 @@
     }
 
     Region outDirtyRegion;
-    if (mQueuedFrames > 0 || mSingleBufferMode) {
+    if (mQueuedFrames > 0 || mAutoRefresh) {
 
         // if we've already called updateTexImage() without going through
         // a composition step, we have to skip this layer at this point
@@ -1507,7 +1507,7 @@
         // buffer mode.
         bool queuedBuffer = false;
         status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
-                mFlinger->mPrimaryDispSync, &mSingleBufferMode, &queuedBuffer,
+                mFlinger->mPrimaryDispSync, &mAutoRefresh, &queuedBuffer,
                 mLastFrameNumberReceived);
         if (updateResult == BufferQueue::PRESENT_LATER) {
             // Producer doesn't want buffer to be displayed yet.  Signal a
@@ -1563,7 +1563,7 @@
         // Decrement the queued-frames count.  Signal another event if we
         // have more frames pending.
         if ((queuedBuffer && android_atomic_dec(&mQueuedFrames) > 1)
-                || mSingleBufferMode) {
+                || mAutoRefresh) {
             mFlinger->signalLayerUpdate();
         }
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index d91e94e..1773daf 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -307,7 +307,7 @@
      * Returns if a frame is queued.
      */
     bool hasQueuedFrame() const { return mQueuedFrames > 0 ||
-            mSidebandStreamChanged || mSingleBufferMode; }
+            mSidebandStreamChanged || mAutoRefresh; }
 
     // -----------------------------------------------------------------------
 
@@ -493,7 +493,7 @@
     std::atomic<uint64_t> mLastFrameNumberReceived;
     bool mUpdateTexImageFailed; // This is only modified from the main thread
 
-    bool mSingleBufferMode;
+    bool mAutoRefresh;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index efc44ab..e161d9f 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -135,6 +135,10 @@
     return mProducer->setSingleBufferMode(singleBufferMode);
 }
 
+status_t MonitoredProducer::setAutoRefresh(bool autoRefresh) {
+    return mProducer->setAutoRefresh(autoRefresh);
+}
+
 status_t MonitoredProducer::setDequeueTimeout(nsecs_t timeout) {
     return mProducer->setDequeueTimeout(timeout);
 }
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index aea2e39..35ce558 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -60,7 +60,8 @@
     virtual uint64_t getNextFrameNumber() const override;
     virtual status_t setDequeueTimeout(nsecs_t timeout) override;
     virtual IBinder* onAsBinder();
-    virtual status_t setSingleBufferMode(bool singleBufferMode);
+    virtual status_t setSingleBufferMode(bool singleBufferMode) override;
+    virtual status_t setAutoRefresh(bool autoRefresh) override;
 
 private:
     sp<IGraphicBufferProducer> mProducer;
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index 5722fb4..d3b66e6 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -32,7 +32,7 @@
 // ---------------------------------------------------------------------------
 
 status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter,
-        const DispSync& dispSync, bool* singleBufferMode, bool* queuedBuffer,
+        const DispSync& dispSync, bool* autoRefresh, bool* queuedBuffer,
         uint64_t maxFrameNumber)
 {
     ATRACE_CALL();
@@ -78,8 +78,8 @@
         return BUFFER_REJECTED;
     }
 
-    if (singleBufferMode) {
-        *singleBufferMode = item.mSingleBufferMode;
+    if (autoRefresh) {
+        *autoRefresh = item.mAutoRefresh;
     }
 
     if (queuedBuffer) {
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
index 207c243..f3942ab 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -57,7 +57,7 @@
     // this does not guarantee that the buffer has been bound to the GL
     // texture.
     status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync,
-            bool* singleBufferMode, bool* queuedBuffer,
+            bool* autoRefresh, bool* queuedBuffer,
             uint64_t maxFrameNumber = 0);
 
     // See GLConsumer::bindTextureImageLocked().