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
diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp
index 159f2bd..577cba9 100644
--- a/libs/vr/libbufferhub/buffer_hub_client.cpp
+++ b/libs/vr/libbufferhub/buffer_hub_client.cpp
@@ -42,11 +42,13 @@
 BufferHubBuffer::BufferHubBuffer(LocalChannelHandle channel_handle)
     : Client{pdx::default_transport::ClientChannel::Create(
           std::move(channel_handle))},
-      id_(-1) {}
+      id_(-1),
+      cid_(-1) {}
 BufferHubBuffer::BufferHubBuffer(const std::string& endpoint_path)
     : Client{pdx::default_transport::ClientChannelFactory::Create(
           endpoint_path)},
-      id_(-1) {}
+      id_(-1),
+      cid_(-1) {}
 
 BufferHubBuffer::~BufferHubBuffer() {
   if (metadata_header_ != nullptr) {
@@ -136,6 +138,7 @@
   }
 
   id_ = new_id;
+  cid_ = buffer_desc.buffer_cid();
   buffer_state_bit_ = buffer_desc.buffer_state_bit();
 
   // Note that here the buffer state is mapped from shared memory as an atomic
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
index 0ea77c8..b71f5dc 100644
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
@@ -107,8 +107,14 @@
   IonBuffer* buffer() { return &buffer_; }
   const IonBuffer* buffer() const { return &buffer_; }
 
+  // Gets ID of the buffer client. All BufferHubBuffer clients derived from the
+  // same buffer in bufferhubd share the same buffer id.
   int id() const { return id_; }
 
+  // Gets the channel id of the buffer client. Each BufferHubBuffer client has
+  // its system unique channel id.
+  int cid() const { return cid_; }
+
   // Returns the buffer buffer state.
   uint64_t buffer_state() { return buffer_state_->load(); };
 
@@ -170,6 +176,7 @@
   // for logging and debugging purposes only and should not be used for lookup
   // or any other functional purpose as a security precaution.
   int id_;
+  int cid_;
   uint64_t buffer_state_bit_{0ULL};
   IonBuffer buffer_;
   IonBuffer metadata_buffer_;
diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
index f4918c4..088a235 100644
--- a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
+++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
@@ -164,10 +164,11 @@
  public:
   BufferDescription() = default;
   BufferDescription(const IonBuffer& buffer, const IonBuffer& metadata, int id,
-                    uint64_t buffer_state_bit,
+                    int buffer_cid, uint64_t buffer_state_bit,
                     const FileHandleType& acquire_fence_fd,
                     const FileHandleType& release_fence_fd)
       : id_(id),
+        buffer_cid_(buffer_cid),
         buffer_state_bit_(buffer_state_bit),
         buffer_(buffer, id),
         metadata_(metadata, id),
@@ -180,6 +181,9 @@
   // ID of the buffer client. All BufferHubBuffer clients derived from the same
   // buffer in bufferhubd share the same buffer id.
   int id() const { return id_; }
+  // Channel ID of the buffer client. Each BufferHubBuffer client has its system
+  // unique channel id.
+  int buffer_cid() const { return buffer_cid_; }
   // State mask of the buffer client. Each BufferHubBuffer client backed by the
   // same buffer channel has uniqued state bit among its siblings. For a
   // producer buffer the bit must be kProducerStateBit; for a consumer the bit
@@ -193,6 +197,7 @@
 
  private:
   int id_{-1};
+  int buffer_cid_{-1};
   uint64_t buffer_state_bit_{0};
   // Two IonBuffers: one for the graphic buffer and one for metadata.
   NativeBufferHandle<FileHandleType> buffer_;
@@ -202,7 +207,7 @@
   FileHandleType acquire_fence_fd_;
   FileHandleType release_fence_fd_;
 
-  PDX_SERIALIZABLE_MEMBERS(BufferDescription<FileHandleType>, id_,
+  PDX_SERIALIZABLE_MEMBERS(BufferDescription<FileHandleType>, id_, buffer_cid_,
                            buffer_state_bit_, buffer_, metadata_,
                            acquire_fence_fd_, release_fence_fd_);
 
@@ -381,6 +386,7 @@
     kOpCreateConsumerQueue,
     kOpGetQueueInfo,
     kOpProducerQueueAllocateBuffers,
+    kOpProducerQueueInsertBuffer,
     kOpProducerQueueRemoveBuffer,
     kOpConsumerQueueImportBuffers,
     // TODO(b/77153033): Separate all those RPC operations into subclasses.
@@ -430,6 +436,8 @@
                     std::vector<std::pair<LocalChannelHandle, size_t>>(
                         uint32_t width, uint32_t height, uint32_t layer_count,
                         uint32_t format, uint64_t usage, size_t buffer_count));
+  PDX_REMOTE_METHOD(ProducerQueueInsertBuffer, kOpProducerQueueInsertBuffer,
+                    size_t(int buffer_cid));
   PDX_REMOTE_METHOD(ProducerQueueRemoveBuffer, kOpProducerQueueRemoveBuffer,
                     void(size_t slot));
   PDX_REMOTE_METHOD(ConsumerQueueImportBuffers, kOpConsumerQueueImportBuffers,
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
index 8feb1cd..1f2c517 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -524,6 +524,40 @@
   return BufferHubQueue::Enqueue({buffer, slot, 0ULL});
 }
 
+Status<size_t> ProducerQueue::InsertBuffer(
+    const std::shared_ptr<BufferProducer>& buffer) {
+  if (buffer == nullptr ||
+      !BufferHubDefs::IsBufferGained(buffer->buffer_state())) {
+    ALOGE(
+        "ProducerQueue::InsertBuffer: Can only insert a buffer when it's in "
+        "gained state.");
+    return ErrorStatus(EINVAL);
+  }
+
+  auto status_or_slot =
+      InvokeRemoteMethod<BufferHubRPC::ProducerQueueInsertBuffer>(
+          buffer->cid());
+  if (!status_or_slot) {
+    ALOGE(
+        "ProducerQueue::InsertBuffer: Failed to insert producer buffer: "
+        "buffer_cid=%d, error: %s.",
+        buffer->cid(), status_or_slot.GetErrorMessage().c_str());
+    return status_or_slot.error_status();
+  }
+
+  size_t slot = status_or_slot.get();
+
+  // Note that we are calling AddBuffer() from the base class to explicitly
+  // avoid Enqueue() the BufferProducer.
+  auto status = BufferHubQueue::AddBuffer(buffer, slot);
+  if (!status) {
+    ALOGE("ProducerQueue::InsertBuffer: Failed to add buffer: %s.",
+          status.GetErrorMessage().c_str());
+    return status.error_status();
+  }
+  return {slot};
+}
+
 Status<void> ProducerQueue::RemoveBuffer(size_t slot) {
   auto status =
       InvokeRemoteMethod<BufferHubRPC::ProducerQueueRemoveBuffer>(slot);
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
index 60e1c4b..df500b4 100644
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
@@ -331,6 +331,13 @@
   pdx::Status<void> AddBuffer(const std::shared_ptr<BufferProducer>& buffer,
                               size_t slot);
 
+  // Inserts a ProducerBuffer into the queue. On success, the method returns the
+  // |slot| number where the new buffer gets inserted. Note that the buffer
+  // being inserted should be in Gain'ed state prior to the call and it's
+  // considered as already Dequeued when the function returns.
+  pdx::Status<size_t> InsertBuffer(
+      const std::shared_ptr<BufferProducer>& buffer);
+
   // Remove producer buffer from the queue.
   pdx::Status<void> RemoveBuffer(size_t slot) override;
 
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
index 47a2734..2975f56 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
@@ -181,6 +181,40 @@
   }
 }
 
+TEST_F(BufferHubQueueTest, TestInsertBuffer) {
+  ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
+
+  consumer_queue_ = producer_queue_->CreateConsumerQueue();
+  ASSERT_TRUE(consumer_queue_ != nullptr);
+  EXPECT_EQ(producer_queue_->capacity(), 0);
+  EXPECT_EQ(consumer_queue_->capacity(), 0);
+
+  std::shared_ptr<BufferProducer> p1 = BufferProducer::Create(
+      kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage, 0);
+  ASSERT_TRUE(p1 != nullptr);
+
+  // Inserting a posted buffer will fail.
+  DvrNativeBufferMetadata meta;
+  EXPECT_EQ(p1->PostAsync(&meta, LocalHandle()), 0);
+  auto status_or_slot = producer_queue_->InsertBuffer(p1);
+  EXPECT_FALSE(status_or_slot.ok());
+  EXPECT_EQ(status_or_slot.error(), EINVAL);
+
+  // Inserting a gained buffer will succeed.
+  std::shared_ptr<BufferProducer> p2 = BufferProducer::Create(
+      kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage);
+  ASSERT_TRUE(p2 != nullptr);
+  status_or_slot = producer_queue_->InsertBuffer(p2);
+  EXPECT_TRUE(status_or_slot.ok());
+  // This is the first buffer inserted, should take slot 0.
+  size_t slot = status_or_slot.get();
+  EXPECT_EQ(slot, 0);
+
+  // Wait and expect the consumer to kick up the newly inserted buffer.
+  WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs);
+  EXPECT_EQ(consumer_queue_->capacity(), 1ULL);
+}
+
 TEST_F(BufferHubQueueTest, TestRemoveBuffer) {
   ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
   DvrNativeBufferMetadata mo;
diff --git a/libs/vr/libpdx/private/pdx/service.h b/libs/vr/libpdx/private/pdx/service.h
index 13aa3e9..15fa327 100644
--- a/libs/vr/libpdx/private/pdx/service.h
+++ b/libs/vr/libpdx/private/pdx/service.h
@@ -59,9 +59,18 @@
   virtual ~Channel() {}
 
   /*
+   * Accessors to the pid of the last active client.
+   */
+  pid_t GetActiveProcessId() const { return client_pid_; }
+  void SetActiveProcessId(pid_t pid) { client_pid_ = pid; }
+
+  /*
    * Utility to get a shared_ptr reference from the channel context pointer.
    */
   static std::shared_ptr<Channel> GetFromMessageInfo(const MessageInfo& info);
+
+ private:
+  pid_t client_pid_ = 0;
 };
 
 /*
diff --git a/libs/vr/libpdx_uds/service_endpoint.cpp b/libs/vr/libpdx_uds/service_endpoint.cpp
index 32d40e8..ecbfdba 100644
--- a/libs/vr/libpdx_uds/service_endpoint.cpp
+++ b/libs/vr/libpdx_uds/service_endpoint.cpp
@@ -521,6 +521,9 @@
   info.flags = 0;
   info.service = service_;
   info.channel = GetChannelState(channel_id);
+  if (info.channel != nullptr) {
+    info.channel->SetActiveProcessId(request.cred.pid);
+  }
   info.send_len = request.send_len;
   info.recv_len = request.max_recv_len;
   info.fd_count = request.file_descriptors.size();