Implement BufferHubProducer::attachBuffer

1/ Introduce new kOpProducerQueueInsertBuffer operation to insert a
   standalone ProducerChannel into a ProducerQueueChannel.
2/ Introduce some PDX security check against channel_id spoofing.

Bug: 69981968
Bug: 79224574
Test: buffer_hub_queue-test, libgui_test
Change-Id: I3c13e2897476c34e6e939756b079fe3440937236
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index b29c1d5..ef3e592 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -123,6 +123,7 @@
         "android.hardware.graphics.common@1.1",
         "libsync",
         "libbinder",
+        "libbufferhub",
         "libbufferhubqueue",  // TODO(b/70046255): Remove this once BufferHub is integrated into libgui.
         "libpdx_default_transport",
         "libcutils",
@@ -149,6 +150,7 @@
                 "BufferHubProducer.cpp",
             ],
             exclude_shared_libs: [
+                "libbufferhub",
                 "libbufferhubqueue",
                 "libpdx_default_transport",
             ],
diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp
index 2bc194a..06d597c 100644
--- a/libs/gui/BufferHubProducer.cpp
+++ b/libs/gui/BufferHubProducer.cpp
@@ -18,6 +18,7 @@
 #include <gui/BufferHubProducer.h>
 #include <inttypes.h>
 #include <log/log.h>
+#include <private/dvr/detached_buffer.h>
 #include <system/window.h>
 #include <ui/DetachedBufferHandle.h>
 
@@ -363,13 +364,86 @@
     return error;
 }
 
-status_t BufferHubProducer::attachBuffer(int* /* out_slot */,
-                                         const sp<GraphicBuffer>& /* buffer */) {
-    // With this BufferHub backed implementation, we assume (for now) all buffers
-    // are allocated and owned by the BufferHub. Thus the attempt of transfering
-    // ownership of a buffer to the buffer queue is intentionally unsupported.
-    LOG_ALWAYS_FATAL("BufferHubProducer::attachBuffer not supported.");
-    return INVALID_OPERATION;
+status_t BufferHubProducer::attachBuffer(int* out_slot, const sp<GraphicBuffer>& buffer) {
+    // In the BufferHub design, all buffers are allocated and owned by the BufferHub. Thus only
+    // GraphicBuffers that are originated from BufferHub can be attached to a BufferHubProducer.
+    ALOGV("queueBuffer: buffer=%p", buffer.get());
+
+    if (out_slot == nullptr) {
+        ALOGE("attachBuffer: out_slot cannot be NULL.");
+        return BAD_VALUE;
+    }
+    if (buffer == nullptr || !buffer->isDetachedBuffer()) {
+        ALOGE("attachBuffer: invalid GraphicBuffer.");
+        return BAD_VALUE;
+    }
+
+    std::unique_lock<std::mutex> lock(mutex_);
+
+    if (connected_api_ == kNoConnectedApi) {
+        ALOGE("attachBuffer: BufferQueue has no connected producer");
+        return NO_INIT;
+    }
+
+    // Before attaching the buffer, caller is supposed to call
+    // IGraphicBufferProducer::setGenerationNumber to inform the
+    // BufferHubProducer the next generation number.
+    if (buffer->getGenerationNumber() != generation_number_) {
+        ALOGE("attachBuffer: Mismatched generation number, buffer: %u, queue: %u.",
+              buffer->getGenerationNumber(), generation_number_);
+        return BAD_VALUE;
+    }
+
+    // Creates a BufferProducer from the GraphicBuffer.
+    std::unique_ptr<DetachedBufferHandle> detached_handle = buffer->takeDetachedBufferHandle();
+    if (detached_handle == nullptr) {
+        ALOGE("attachBuffer: DetachedBufferHandle cannot be NULL.");
+        return BAD_VALUE;
+    }
+    auto detached_buffer = DetachedBuffer::Import(std::move(detached_handle->handle()));
+    if (detached_buffer == nullptr) {
+        ALOGE("attachBuffer: DetachedBuffer cannot be NULL.");
+        return BAD_VALUE;
+    }
+    auto status_or_handle = detached_buffer->Promote();
+    if (!status_or_handle.ok()) {
+        ALOGE("attachBuffer: Failed to promote a DetachedBuffer into a BufferProducer, error=%d.",
+              status_or_handle.error());
+        return BAD_VALUE;
+    }
+    std::shared_ptr<BufferProducer> buffer_producer =
+            BufferProducer::Import(status_or_handle.take());
+    if (buffer_producer == nullptr) {
+        ALOGE("attachBuffer: Failed to import BufferProducer.");
+        return BAD_VALUE;
+    }
+
+    // Adds the BufferProducer into the Queue.
+    auto status_or_slot = queue_->InsertBuffer(buffer_producer);
+    if (!status_or_slot.ok()) {
+        ALOGE("attachBuffer: Failed to insert buffer, error=%d.", status_or_slot.error());
+        return BAD_VALUE;
+    }
+
+    size_t slot = status_or_slot.get();
+    ALOGV("attachBuffer: returning slot %zu.", slot);
+    if (slot >= static_cast<size_t>(max_buffer_count_)) {
+        ALOGE("attachBuffer: Invalid slot: %zu.", slot);
+        return BAD_VALUE;
+    }
+
+    // The just attached buffer should be in dequeued state according to IGraphicBufferProducer
+    // interface. In BufferHub's language the buffer should be in Gained state.
+    buffers_[slot].mGraphicBuffer = buffer;
+    buffers_[slot].mBufferState.attachProducer();
+    buffers_[slot].mEglFence = EGL_NO_SYNC_KHR;
+    buffers_[slot].mFence = Fence::NO_FENCE;
+    buffers_[slot].mRequestBufferCalled = true;
+    buffers_[slot].mAcquireCalled = false;
+    buffers_[slot].mNeedsReallocation = false;
+
+    *out_slot = static_cast<int>(slot);
+    return NO_ERROR;
 }
 
 status_t BufferHubProducer::queueBuffer(int slot, const QueueBufferInput& input,
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index e5a4adb..c20e8fc 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -787,10 +787,31 @@
 
     ASSERT_OK(mProducer->disconnect(TEST_API));
 
-    if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) {
-        // TODO(b/69981968): Implement BufferHubProducer::attachBuffer
-        ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer));
+    ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer));
+}
+
+TEST_P(IGraphicBufferProducerTest, DetachThenAttach_Succeeds) {
+    int slot = -1;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buffer;
+
+    setupDequeueRequestBuffer(&slot, &fence, &buffer);
+    ASSERT_TRUE(buffer != nullptr);
+
+    ASSERT_OK(mProducer->detachBuffer(slot));
+    EXPECT_OK(buffer->initCheck());
+
+    if (GetParam() == USE_BUFFER_HUB_PRODUCER) {
+        // For a GraphicBuffer backed by BufferHub, once detached from an IGBP, it should have
+        // isDetachedBuffer() set. Note that this only applies to BufferHub.
+        EXPECT_TRUE(buffer->isDetachedBuffer());
+    } else {
+        EXPECT_FALSE(buffer->isDetachedBuffer());
     }
+
+    EXPECT_OK(mProducer->attachBuffer(&slot, buffer));
+    EXPECT_FALSE(buffer->isDetachedBuffer());
+    EXPECT_OK(buffer->initCheck());
 }
 
 #if USE_BUFFER_HUB_AS_BUFFER_QUEUE