BQ: Prevent operations on disconnected BQs

- Update unit tests to match

Bug 23763412

Change-Id: I77e59bf6b57b328433c3835450455f80a8fa454b
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index 7827072..267c50f 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -117,7 +117,7 @@
     //
     // The buffer will not be overwritten until the fence signals.  The fence
     // will usually be the one obtained from dequeueBuffer.
-    virtual void cancelBuffer(int slot, const sp<Fence>& fence);
+    virtual status_t cancelBuffer(int slot, const sp<Fence>& fence);
 
     // Query native window attributes.  The "what" values are enumerated in
     // window.h (e.g. NATIVE_WINDOW_FORMAT).
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 5b84bdc..6885eb0 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -74,7 +74,8 @@
     // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
     //
     // Return of a value other than NO_ERROR means an error has occurred:
-    // * NO_INIT - the buffer queue has been abandoned.
+    // * NO_INIT - the buffer queue has been abandoned or the producer is not
+    //             connected.
     // * BAD_VALUE - one of the two conditions occurred:
     //              * slot was out of range (see above)
     //              * buffer specified by the slot is not dequeued
@@ -170,7 +171,8 @@
     // success.
     //
     // Return of a negative means an error has occurred:
-    // * NO_INIT - the buffer queue has been abandoned.
+    // * NO_INIT - the buffer queue has been abandoned or the producer is not
+    //             connected.
     // * BAD_VALUE - both in async mode and buffer count was less than the
     //               max numbers of buffers that can be allocated at once.
     // * INVALID_OPERATION - cannot attach the buffer because it would cause
@@ -198,7 +200,8 @@
     // requestBuffer).
     //
     // Return of a value other than NO_ERROR means an error has occurred:
-    // * NO_INIT - the buffer queue has been abandoned.
+    // * NO_INIT - the buffer queue has been abandoned or the producer is not
+    //             connected.
     // * BAD_VALUE - the given slot number is invalid, either because it is
     //               out of the range [0, NUM_BUFFER_SLOTS), or because the slot
     //               it refers to is not currently dequeued and requested.
@@ -218,7 +221,8 @@
     // equivalent to fence from the dequeueBuffer call.
     //
     // Return of a value other than NO_ERROR means an error has occurred:
-    // * NO_INIT - the buffer queue has been abandoned.
+    // * NO_INIT - the buffer queue has been abandoned or the producer is not
+    //             connected.
     // * BAD_VALUE - either outBuffer or outFence were NULL.
     // * NO_MEMORY - no slots were found that were both free and contained a
     //               GraphicBuffer.
@@ -237,7 +241,8 @@
     // success.
     //
     // Return of a negative value means an error has occurred:
-    // * NO_INIT - the buffer queue has been abandoned.
+    // * NO_INIT - the buffer queue has been abandoned or the producer is not
+    //             connected.
     // * BAD_VALUE - outSlot or buffer were NULL, invalid combination of
     //               async mode and buffer count override, or the generation
     //               number of the buffer did not match the buffer queue.
@@ -271,7 +276,8 @@
     // (refer to the documentation below).
     //
     // Return of a value other than NO_ERROR means an error has occurred:
-    // * NO_INIT - the buffer queue has been abandoned.
+    // * NO_INIT - the buffer queue has been abandoned or the producer is not
+    //             connected.
     // * BAD_VALUE - one of the below conditions occurred:
     //              * fence was NULL
     //              * scaling mode was unknown
@@ -384,9 +390,19 @@
     //
     // The buffer is not queued for use by the consumer.
     //
+    // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+    //
     // The buffer will not be overwritten until the fence signals.  The fence
     // will usually be the one obtained from dequeueBuffer.
-    virtual void cancelBuffer(int slot, const sp<Fence>& fence) = 0;
+    //
+    // Return of a value other than NO_ERROR means an error has occurred:
+    // * NO_INIT - the buffer queue has been abandoned or the producer is not
+    //             connected.
+    // * BAD_VALUE - one of the below conditions occurred:
+    //              * fence was NULL
+    //              * slot index was out of range (see above).
+    //              * the slot was not in the dequeued state
+    virtual status_t cancelBuffer(int slot, const sp<Fence>& fence) = 0;
 
     // query retrieves some information for this surface
     // 'what' tokens allowed are that of NATIVE_WINDOW_* in <window.h>
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 8cc0cfa..06cdeab 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -57,6 +57,11 @@
         return NO_INIT;
     }
 
+    if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+        BQ_LOGE("requestBuffer: BufferQueue has no connected producer");
+        return NO_INIT;
+    }
+
     if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
         BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)",
                 slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
@@ -286,6 +291,16 @@
     { // Autolock scope
         Mutex::Autolock lock(mCore->mMutex);
         mConsumerName = mCore->mConsumerName;
+
+        if (mCore->mIsAbandoned) {
+            BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
+            return NO_INIT;
+        }
+
+        if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+            BQ_LOGE("dequeueBuffer: BufferQueue has no connected producer");
+            return NO_INIT;
+        }
     } // Autolock scope
 
     BQ_LOGV("dequeueBuffer: async=%s w=%u h=%u format=%#x, usage=%#x",
@@ -453,6 +468,11 @@
         return NO_INIT;
     }
 
+    if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+        BQ_LOGE("detachBuffer(P): BufferQueue has no connected producer");
+        return NO_INIT;
+    }
+
     if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
         BQ_LOGE("detachBuffer(P): slot index %d out of range [0, %d)",
                 slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
@@ -487,13 +507,19 @@
     }
 
     Mutex::Autolock lock(mCore->mMutex);
-    mCore->waitWhileAllocatingLocked();
 
     if (mCore->mIsAbandoned) {
         BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
         return NO_INIT;
     }
 
+    if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+        BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer");
+        return NO_INIT;
+    }
+
+    mCore->waitWhileAllocatingLocked();
+
     if (mCore->mFreeBuffers.empty()) {
         return NO_MEMORY;
     }
@@ -524,7 +550,16 @@
     }
 
     Mutex::Autolock lock(mCore->mMutex);
-    mCore->waitWhileAllocatingLocked();
+
+    if (mCore->mIsAbandoned) {
+        BQ_LOGE("attachBuffer(P): BufferQueue has been abandoned");
+        return NO_INIT;
+    }
+
+    if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+        BQ_LOGE("attachBuffer(P): BufferQueue has no connected producer");
+        return NO_INIT;
+    }
 
     if (buffer->getGenerationNumber() != mCore->mGenerationNumber) {
         BQ_LOGE("attachBuffer: generation number mismatch [buffer %u] "
@@ -533,6 +568,8 @@
         return BAD_VALUE;
     }
 
+    mCore->waitWhileAllocatingLocked();
+
     status_t returnFlags = NO_ERROR;
     int found;
     // TODO: Should we provide an async flag to attachBuffer? It seems
@@ -612,6 +649,11 @@
             return NO_INIT;
         }
 
+        if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+            BQ_LOGE("queueBuffer: BufferQueue has no connected producer");
+            return NO_INIT;
+        }
+
         const int maxBufferCount = mCore->getMaxBufferCountLocked(async);
 
         if (slot < 0 || slot >= maxBufferCount) {
@@ -748,27 +790,32 @@
     return NO_ERROR;
 }
 
-void BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
+status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
     ATRACE_CALL();
     BQ_LOGV("cancelBuffer: slot %d", slot);
     Mutex::Autolock lock(mCore->mMutex);
 
     if (mCore->mIsAbandoned) {
         BQ_LOGE("cancelBuffer: BufferQueue has been abandoned");
-        return;
+        return NO_INIT;
+    }
+
+    if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+        BQ_LOGE("cancelBuffer: BufferQueue has no connected producer");
+        return NO_INIT;
     }
 
     if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
         BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)",
                 slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
-        return;
+        return BAD_VALUE;
     } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
         BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
                 "(state = %d)", slot, mSlots[slot].mBufferState);
-        return;
+        return BAD_VALUE;
     } else if (fence == NULL) {
         BQ_LOGE("cancelBuffer: fence is NULL");
-        return;
+        return BAD_VALUE;
     }
 
     mCore->mFreeBuffers.push_front(slot);
@@ -776,6 +823,8 @@
     mSlots[slot].mFence = fence;
     mCore->mDequeueCondition.broadcast();
     mCore->validateConsistencyLocked();
+
+    return NO_ERROR;
 }
 
 int BufferQueueProducer::query(int what, int *outValue) {
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 6495aa9..3cdcd79 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -206,12 +206,17 @@
         return result;
     }
 
-    virtual void cancelBuffer(int buf, const sp<Fence>& fence) {
+    virtual status_t cancelBuffer(int buf, const sp<Fence>& fence) {
         Parcel data, reply;
         data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
         data.writeInt32(buf);
         data.write(*fence.get());
-        remote()->transact(CANCEL_BUFFER, data, &reply);
+        status_t result = remote()->transact(CANCEL_BUFFER, data, &reply);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.readInt32();
+        return result;
     }
 
     virtual int query(int what, int* value) {
@@ -434,7 +439,8 @@
             int buf = data.readInt32();
             sp<Fence> fence = new Fence();
             data.read(*fence.get());
-            cancelBuffer(buf, fence);
+            status_t result = cancelBuffer(buf, fence);
+            reply->writeInt32(result);
             return NO_ERROR;
         }
         case QUERY: {
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index d559e54..289cc74 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -449,6 +449,9 @@
         const CpuConsumerTestParams& params,
         int maxBufferSlack) {
     status_t err;
+    err = native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU);
+    ASSERT_NO_ERROR(err, "connect error: ");
+
     err = native_window_set_buffers_dimensions(anw.get(),
             params.width, params.height);
     ASSERT_NO_ERROR(err, "set_buffers_dimensions error: ");
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index d6bd8a8..34fb998 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -208,6 +208,27 @@
         return mProducer->dequeueBuffer(&result->slot, &result->fence, async, w, h, format, usage);
     }
 
+    void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence,
+            sp<GraphicBuffer> *buffer)
+    {
+        ASSERT_TRUE(slot != NULL);
+        ASSERT_TRUE(fence != NULL);
+        ASSERT_TRUE(buffer != NULL);
+
+        ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+        ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                (mProducer->dequeueBuffer(slot, fence,
+                QUEUE_BUFFER_INPUT_ASYNC, DEFAULT_WIDTH, DEFAULT_HEIGHT,
+                DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)));
+
+        EXPECT_LE(0, *slot);
+        EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, *slot);
+
+        // Request the buffer (pre-requisite for queueing)
+        ASSERT_OK(mProducer->requestBuffer(*slot, buffer));
+    }
+
 private: // hide from test body
     sp<DummyConsumer> mDC;
 
@@ -334,12 +355,11 @@
     int dequeuedSlot = -1;
     sp<Fence> dequeuedFence;
 
-    // XX: OK to assume first call returns this flag or not? Not really documented.
-    ASSERT_EQ(OK | IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
+    ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+            (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                                      QUEUE_BUFFER_INPUT_ASYNC,
                                      DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                                     TEST_PRODUCER_USAGE_BITS));
+                                     TEST_PRODUCER_USAGE_BITS)));
 
     EXPECT_LE(0, dequeuedSlot);
     EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, dequeuedSlot);
@@ -400,11 +420,11 @@
     int dequeuedSlot = -1;
     sp<Fence> dequeuedFence;
 
-    ASSERT_EQ(OK | IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
+    ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+            (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                                      QUEUE_BUFFER_INPUT_ASYNC,
                                      DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                                     TEST_PRODUCER_USAGE_BITS));
+                                     TEST_PRODUCER_USAGE_BITS)));
 
     // Slot was enqueued without requesting a buffer
     {
@@ -470,11 +490,11 @@
     int dequeuedSlot = -1;
     sp<Fence> dequeuedFence;
 
-    ASSERT_EQ(OK | IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
-            mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
+    ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+            (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                                      QUEUE_BUFFER_INPUT_ASYNC,
                                      DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                                     TEST_PRODUCER_USAGE_BITS));
+                                     TEST_PRODUCER_USAGE_BITS)));
 
     // No return code, but at least test that it doesn't blow up...
     // TODO: add a return code
@@ -482,6 +502,7 @@
 }
 
 TEST_F(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) {
+    ASSERT_NO_FATAL_FAILURE(ConnectProducer());
     int minUndequeuedBuffers;
     ASSERT_OK(mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
             &minUndequeuedBuffers));
@@ -499,10 +520,10 @@
     for (int i = 0; i < minBuffers; ++i) {
         DequeueBufferResult result;
 
-        EXPECT_LE(OK,
-                dequeueBuffer(QUEUE_BUFFER_INPUT_ASYNC,
+        EXPECT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                (dequeueBuffer(QUEUE_BUFFER_INPUT_ASYNC,
                               DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                              TEST_PRODUCER_USAGE_BITS, &result))
+                              TEST_PRODUCER_USAGE_BITS, &result)))
                 << "iteration: " << i << ", slot: " << result.slot;
 
         dequeueList.push_back(result);
@@ -520,17 +541,18 @@
         int dequeuedSlot = -1;
         sp<Fence> dequeuedFence;
 
-        EXPECT_LE(OK,
-                mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
+        EXPECT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                                          QUEUE_BUFFER_INPUT_ASYNC,
                                          DEFAULT_WIDTH, DEFAULT_HEIGHT,
                                          DEFAULT_FORMAT,
-                                         TEST_PRODUCER_USAGE_BITS))
+                                         TEST_PRODUCER_USAGE_BITS)))
                 << "iteration: " << i << ", slot: " << dequeuedSlot;
     }
 }
 
 TEST_F(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Fails) {
+    ASSERT_NO_FATAL_FAILURE(ConnectProducer());
     int minUndequeuedBuffers;
     ASSERT_OK(mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
                                &minUndequeuedBuffers));
@@ -550,12 +572,12 @@
         int dequeuedSlot = -1;
         sp<Fence> dequeuedFence;
 
-        ASSERT_LE(OK,
-                mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
+        ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                                          QUEUE_BUFFER_INPUT_ASYNC,
                                          DEFAULT_WIDTH, DEFAULT_HEIGHT,
                                          DEFAULT_FORMAT,
-                                         TEST_PRODUCER_USAGE_BITS))
+                                         TEST_PRODUCER_USAGE_BITS)))
                 << "slot: " << dequeuedSlot;
     }
 
@@ -574,6 +596,7 @@
 
 TEST_F(IGraphicBufferProducerTest, SetAsyncMode_Succeeds) {
     ASSERT_OK(mConsumer->setMaxAcquiredBufferCount(1)) << "maxAcquire: " << 1;
+    ASSERT_NO_FATAL_FAILURE(ConnectProducer());
     ASSERT_OK(mProducer->setAsyncMode(true)) << "async mode: " << true;
     ASSERT_OK(mProducer->setMaxDequeuedBufferCount(1)) << "maxDequeue: " << 1;
 
@@ -589,9 +612,10 @@
     // Should now be able to queue/dequeue as many buffers as we want without
     // blocking
     for (int i = 0; i < 5; ++i) {
-        ASSERT_LE(OK, mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
+        ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                 true, DEFAULT_WIDTH, DEFAULT_HEIGHT,
-                DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)) << "slot : "
+                DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS))) << "slot : "
                 << dequeuedSlot;
         ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer));
         ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output));
@@ -599,14 +623,16 @@
 }
 
 TEST_F(IGraphicBufferProducerTest, SetAsyncMode_Fails) {
+    ASSERT_NO_FATAL_FAILURE(ConnectProducer());
     // Prerequisite to fail out a valid setBufferCount call
     {
         int dequeuedSlot = -1;
         sp<Fence> dequeuedFence;
 
-        ASSERT_LE(OK, mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
+        ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                 QUEUE_BUFFER_INPUT_ASYNC, DEFAULT_WIDTH, DEFAULT_HEIGHT,
-                DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)) << "slot: "
+                DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS))) << "slot: "
                         << dequeuedSlot;
     }
 
@@ -622,4 +648,103 @@
             << false;
 }
 
+TEST_F(IGraphicBufferProducerTest,
+        DisconnectedProducerReturnsError_dequeueBuffer) {
+    int slot = -1;
+    sp<Fence> fence;
+
+    ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence,
+            QUEUE_BUFFER_INPUT_ASYNC, DEFAULT_WIDTH, DEFAULT_HEIGHT,
+            DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS));
+}
+
+TEST_F(IGraphicBufferProducerTest,
+        DisconnectedProducerReturnsError_detachNextBuffer) {
+    sp<Fence> fence;
+    sp<GraphicBuffer> buffer;
+
+    ASSERT_EQ(NO_INIT, mProducer->detachNextBuffer(&buffer, &fence));
+}
+
+TEST_F(IGraphicBufferProducerTest,
+        DisconnectedProducerReturnsError_requestBuffer) {
+    ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+    int slot = -1;
+    sp<Fence> fence;
+
+    ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+            (mProducer->dequeueBuffer(&slot, &fence,
+            QUEUE_BUFFER_INPUT_ASYNC, DEFAULT_WIDTH, DEFAULT_HEIGHT,
+            DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)));
+
+    EXPECT_LE(0, slot);
+    EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, slot);
+
+    ASSERT_OK(mProducer->disconnect(TEST_API));
+
+    sp<GraphicBuffer> buffer;
+
+    ASSERT_EQ(NO_INIT, mProducer->requestBuffer(slot, &buffer));
+}
+
+
+TEST_F(IGraphicBufferProducerTest,
+        DisconnectedProducerReturnsError_detachBuffer) {
+    int slot = -1;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buffer;
+
+    setupDequeueRequestBuffer(&slot, &fence, &buffer);
+
+    ASSERT_OK(mProducer->disconnect(TEST_API));
+
+    ASSERT_EQ(NO_INIT, mProducer->detachBuffer(slot));
+}
+
+TEST_F(IGraphicBufferProducerTest,
+        DisconnectedProducerReturnsError_queueBuffer) {
+    int slot = -1;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buffer;
+
+    setupDequeueRequestBuffer(&slot, &fence, &buffer);
+
+    ASSERT_OK(mProducer->disconnect(TEST_API));
+
+    // A generic "valid" input
+    IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
+    IGraphicBufferProducer::QueueBufferOutput output;
+
+    ASSERT_EQ(NO_INIT, mProducer->queueBuffer(slot, input, &output));
+}
+
+TEST_F(IGraphicBufferProducerTest,
+        DisconnectedProducerReturnsError_cancelBuffer) {
+    int slot = -1;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buffer;
+
+    setupDequeueRequestBuffer(&slot, &fence, &buffer);
+
+    ASSERT_OK(mProducer->disconnect(TEST_API));
+
+    ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, fence));
+}
+
+TEST_F(IGraphicBufferProducerTest,
+        DisconnectedProducerReturnsError_attachBuffer) {
+    int slot = -1;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buffer;
+
+    setupDequeueRequestBuffer(&slot, &fence, &buffer);
+
+    ASSERT_OK(mProducer->detachBuffer(slot));
+
+    ASSERT_OK(mProducer->disconnect(TEST_API));
+
+    ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer));
+}
+
 } // namespace android
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index c61eb1a..2356f54 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -216,6 +216,7 @@
 }
 
 TEST_F(SurfaceTextureClientTest, DefaultGeometryValues) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ANativeWindowBuffer* buf;
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf));
     EXPECT_EQ(1, buf->width);
@@ -225,6 +226,7 @@
 }
 
 TEST_F(SurfaceTextureClientTest, BufferGeometryCanBeSet) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ANativeWindowBuffer* buf;
     EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 16, 8));
     EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), PIXEL_FORMAT_RGB_565));
@@ -236,6 +238,7 @@
 }
 
 TEST_F(SurfaceTextureClientTest, BufferGeometryDefaultSizeSetFormat) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ANativeWindowBuffer* buf;
     EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 0, 0));
     EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), PIXEL_FORMAT_RGB_565));
@@ -247,6 +250,7 @@
 }
 
 TEST_F(SurfaceTextureClientTest, BufferGeometrySetSizeDefaultFormat) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ANativeWindowBuffer* buf;
     EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 16, 8));
     EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), 0));
@@ -258,6 +262,7 @@
 }
 
 TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeUnset) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ANativeWindowBuffer* buf;
     EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 16, 8));
     EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), 0));
@@ -276,6 +281,7 @@
 }
 
 TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeChangedWithoutFormat) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ANativeWindowBuffer* buf;
     EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 0, 0));
     EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), PIXEL_FORMAT_RGB_565));
@@ -293,6 +299,7 @@
 }
 
 TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSize) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     sp<GLConsumer> st(mST);
     ANativeWindowBuffer* buf;
     EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8));
@@ -305,6 +312,7 @@
 
 TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeAfterDequeue) {
     ANativeWindowBuffer* buf[2];
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0]));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1]));
@@ -325,6 +333,7 @@
 
 TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeVsGeometry) {
     ANativeWindowBuffer* buf[2];
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4));
     EXPECT_EQ(OK, mST->setDefaultBufferSize(16, 8));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0]));
@@ -351,6 +360,7 @@
 
 TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) {
     android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(OK, mANW->setSwapInterval(mANW.get(), 0));
     ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4));
 
@@ -374,6 +384,7 @@
 
 TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeSlowRetire) {
     android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0]));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1]));
@@ -394,6 +405,7 @@
 
 TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeFastRetire) {
     android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0]));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1]));
@@ -414,8 +426,8 @@
 
 TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDQQR) {
     android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3));
-
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0]));
     ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1));
     EXPECT_EQ(OK, mST->updateTexImage());
@@ -439,6 +451,7 @@
 TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeDequeueCurrent) {
     android_native_buffer_t* buf[3];
     android_native_buffer_t* firstBuf;
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &firstBuf));
     ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), firstBuf, -1));
@@ -458,6 +471,7 @@
 
 TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeMinUndequeued) {
     android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3));
 
     // We should be able to dequeue all the buffers before we've queued mANWy.
@@ -483,6 +497,7 @@
 }
 
 TEST_F(SurfaceTextureClientTest, SetCropCropsCrop) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     android_native_rect_t rect = {-2, -13, 40, 18};
     native_window_set_crop(mANW.get(), &rect);
 
@@ -537,6 +552,7 @@
     };
 
     android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3));
     // dequeue/queue/update so we have a current buffer
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0]));
@@ -560,6 +576,7 @@
 TEST_F(SurfaceTextureClientTest, GetTransformMatrixReturnsVerticalFlip) {
     android_native_buffer_t* buf[3];
     float mtx[16] = {};
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0]));
     ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1));
@@ -590,6 +607,7 @@
 TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffers) {
     android_native_buffer_t* buf[3];
     float mtx[16] = {};
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0]));
     ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1));
@@ -639,6 +657,7 @@
     crop.right = 5;
     crop.bottom = 5;
 
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4));
     ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 8, 8));
     ASSERT_EQ(OK, native_window_set_buffers_format(mANW.get(), 0));
diff --git a/libs/gui/tests/SurfaceTextureFBO_test.cpp b/libs/gui/tests/SurfaceTextureFBO_test.cpp
index c243fc0..0606839 100644
--- a/libs/gui/tests/SurfaceTextureFBO_test.cpp
+++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp
@@ -27,6 +27,8 @@
     const int texWidth = 64;
     const int texHeight = 64;
 
+    ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(),
+            NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
             texWidth, texHeight));
     ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index 0e85b90..1a904b5 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -28,6 +28,8 @@
     const int texWidth = 64;
     const int texHeight = 66;
 
+    ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(),
+            NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
             texWidth, texHeight));
     ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
@@ -76,6 +78,8 @@
     const int texWidth = 64;
     const int texHeight = 64;
 
+    ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(),
+            NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
             texWidth, texHeight));
     ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
@@ -124,6 +128,8 @@
     const int texWidth = 64;
     const int texHeight = 66;
 
+    ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(),
+            NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
             texWidth, texHeight));
     ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
@@ -190,6 +196,8 @@
     enum { texHeight = 16 };
     enum { numFrames = 1024 };
 
+    ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(),
+            NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
             texWidth, texHeight));
     ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
@@ -333,6 +341,8 @@
     const int texWidth = 64;
     const int texHeight = 66;
 
+    ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(),
+            NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
             texWidth, texHeight));
     ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
@@ -377,6 +387,8 @@
     const int texWidth = 64;
     const int texHeight = 64;
 
+    ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(),
+            NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
             texWidth, texHeight));
     ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
@@ -435,7 +447,10 @@
         virtual bool threadLoop() {
             ANativeWindowBuffer* anb;
 
-            native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_EGL);
+            if (native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU) !=
+                    NO_ERROR) {
+                return false;
+            }
 
             for (int numFrames =0 ; numFrames < 2; numFrames ++) {
 
@@ -452,7 +467,10 @@
                 }
             }
 
-            native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_EGL);
+            if (native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU)
+                    != NO_ERROR) {
+                return false;
+            }
 
             return false;
         }
@@ -486,7 +504,7 @@
 // attempt to release a buffer that it does not owned
 TEST_F(SurfaceTextureGLTest, DisconnectClearsCurrentTexture) {
     ASSERT_EQ(OK, native_window_api_connect(mANW.get(),
-            NATIVE_WINDOW_API_EGL));
+            NATIVE_WINDOW_API_CPU));
 
     ANativeWindowBuffer *anb;
 
@@ -500,9 +518,9 @@
     EXPECT_EQ(OK,mST->updateTexImage());
 
     ASSERT_EQ(OK, native_window_api_disconnect(mANW.get(),
-            NATIVE_WINDOW_API_EGL));
+            NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(OK, native_window_api_connect(mANW.get(),
-            NATIVE_WINDOW_API_EGL));
+            NATIVE_WINDOW_API_CPU));
 
     EXPECT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb));
     EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1));
@@ -512,7 +530,7 @@
     EXPECT_EQ(OK,mST->updateTexImage());
 
     ASSERT_EQ(OK, native_window_api_disconnect(mANW.get(),
-            NATIVE_WINDOW_API_EGL));
+            NATIVE_WINDOW_API_CPU));
 }
 
 TEST_F(SurfaceTextureGLTest, ScaleToWindowMode) {
@@ -614,6 +632,11 @@
             Mutex::Autolock lock(mMutex);
             ANativeWindowBuffer* anb;
 
+            if (native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU) !=
+                    NO_ERROR) {
+                return false;
+            }
+
             // Frame 1
             if (native_window_dequeue_buffer_and_wait(mANW.get(),
                     &anb) != NO_ERROR) {
@@ -678,6 +701,9 @@
     int texHeight = 16;
     ANativeWindowBuffer* anb;
 
+    ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(),
+            NATIVE_WINDOW_API_CPU));
+
     GLint maxTextureSize;
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
 
diff --git a/libs/gui/tests/SurfaceTextureMultiContextGL_test.cpp b/libs/gui/tests/SurfaceTextureMultiContextGL_test.cpp
index f74ac3d..5b02dcf 100644
--- a/libs/gui/tests/SurfaceTextureMultiContextGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureMultiContextGL_test.cpp
@@ -26,6 +26,7 @@
 namespace android {
 
 TEST_F(SurfaceTextureMultiContextGLTest, UpdateFromMultipleContextsFails) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Latch the texture contents on the primary context.
@@ -40,6 +41,7 @@
 }
 
 TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextSucceeds) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Latch the texture contents on the primary context.
@@ -55,6 +57,7 @@
 
 TEST_F(SurfaceTextureMultiContextGLTest,
         DetachFromContextSucceedsAfterProducerDisconnect) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Latch the texture contents on the primary context.
@@ -70,6 +73,7 @@
 }
 
 TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWhenAbandoned) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Latch the texture contents on the primary context.
@@ -82,6 +86,7 @@
 }
 
 TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWhenDetached) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Latch the texture contents on the primary context.
@@ -96,6 +101,7 @@
 }
 
 TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWithNoDisplay) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Latch the texture contents on the primary context.
@@ -112,6 +118,7 @@
 }
 
 TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWithNoContext) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Latch the texture contents on the primary context.
@@ -128,6 +135,7 @@
 }
 
 TEST_F(SurfaceTextureMultiContextGLTest, UpdateTexImageFailsWhenDetached) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Detach from the primary context.
@@ -139,6 +147,7 @@
 }
 
 TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextSucceeds) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Latch the texture contents on the primary context.
@@ -169,6 +178,7 @@
 
 TEST_F(SurfaceTextureMultiContextGLTest,
         AttachToContextSucceedsAfterProducerDisconnect) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Latch the texture contents on the primary context.
@@ -200,6 +210,7 @@
 
 TEST_F(SurfaceTextureMultiContextGLTest,
         AttachToContextSucceedsBeforeUpdateTexImage) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Detach from the primary context.
@@ -230,6 +241,7 @@
 }
 
 TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWhenAbandoned) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Latch the texture contents on the primary context.
@@ -247,6 +259,7 @@
 }
 
 TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWhenAttached) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Latch the texture contents on the primary context.
@@ -259,6 +272,7 @@
 
 TEST_F(SurfaceTextureMultiContextGLTest,
         AttachToContextFailsWhenAttachedBeforeUpdateTexImage) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Attempt to attach to the primary context.
@@ -266,6 +280,7 @@
 }
 
 TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWithNoDisplay) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Latch the texture contents on the primary context.
@@ -285,6 +300,7 @@
 }
 
 TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextSucceedsTwice) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Latch the texture contents on the primary context.
@@ -323,6 +339,7 @@
 
 TEST_F(SurfaceTextureMultiContextGLTest,
         AttachToContextSucceedsTwiceBeforeUpdateTexImage) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
     // Detach from the primary context.
@@ -361,6 +378,7 @@
 
 TEST_F(SurfaceTextureMultiContextGLTest,
         UpdateTexImageSucceedsForBufferConsumedBeforeDetach) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
 
     // produce two frames and consume them both on the primary context
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
@@ -387,6 +405,7 @@
 
 TEST_F(SurfaceTextureMultiContextGLTest,
        AttachAfterDisplayTerminatedSucceeds) {
+    ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
 
     // produce two frames and consume them both on the primary context
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 3f495f8..6f0104a 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -100,6 +100,8 @@
     ASSERT_EQ(NO_ERROR, sf->captureScreen(display, producer, Rect(),
             64, 64, 0, 0x7fffffff, false));
 
+    ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(),
+            NATIVE_WINDOW_API_CPU));
     // Set the PROTECTED usage bit and verify that the screenshot fails.  Note
     // that we need to dequeue a buffer in order for it to actually get
     // allocated in SurfaceFlinger.
@@ -190,6 +192,8 @@
     // Allocate a buffer with a generation number of 0
     ANativeWindowBuffer* buffer;
     int fenceFd;
+    ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(),
+            NATIVE_WINDOW_API_CPU));
     ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fenceFd));
     ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fenceFd));
 
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 94f30d5..0880a44 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -476,7 +476,8 @@
     return NO_ERROR;
 }
 
-void VirtualDisplaySurface::cancelBuffer(int pslot, const sp<Fence>& fence) {
+status_t VirtualDisplaySurface::cancelBuffer(int pslot,
+        const sp<Fence>& fence) {
     if (mDisplayId < 0)
         return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
 
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 5807eb1..91e94f1 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -108,7 +108,7 @@
     virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer);
     virtual status_t queueBuffer(int pslot,
             const QueueBufferInput& input, QueueBufferOutput* output);
-    virtual void cancelBuffer(int pslot, const sp<Fence>& fence);
+    virtual status_t cancelBuffer(int pslot, const sp<Fence>& fence);
     virtual int query(int what, int* value);
     virtual status_t connect(const sp<IProducerListener>& listener,
             int api, bool producerControlledByApp, QueueBufferOutput* output);
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index 85182d9..f16ad59 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -89,8 +89,8 @@
     return mProducer->queueBuffer(slot, input, output);
 }
 
-void MonitoredProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
-    mProducer->cancelBuffer(slot, fence);
+status_t MonitoredProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
+    return mProducer->cancelBuffer(slot, fence);
 }
 
 int MonitoredProducer::query(int what, int* value) {
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index f5575ff..0bdc050 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -46,7 +46,7 @@
             const sp<GraphicBuffer>& buffer);
     virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
             QueueBufferOutput* output);
-    virtual void cancelBuffer(int slot, const sp<Fence>& fence);
+    virtual status_t cancelBuffer(int slot, const sp<Fence>& fence);
     virtual int query(int what, int* value);
     virtual status_t connect(const sp<IProducerListener>& token, int api,
             bool producerControlledByApp, QueueBufferOutput* output);