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/services/vr/bufferhubd/detached_buffer_channel.cpp b/services/vr/bufferhubd/detached_buffer_channel.cpp
index a5cf68d..3061805 100644
--- a/services/vr/bufferhubd/detached_buffer_channel.cpp
+++ b/services/vr/bufferhubd/detached_buffer_channel.cpp
@@ -109,6 +109,7 @@
   return BufferDescription<BorrowedHandle>{buffer_,
                                            metadata_buffer_,
                                            buffer_id(),
+                                           channel_id(),
                                            /*buffer_state_bit=*/0,
                                            BorrowedHandle{},
                                            BorrowedHandle{}};
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
index b6977aa..97af660 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -236,9 +236,13 @@
 
 BufferDescription<BorrowedHandle> ProducerChannel::GetBuffer(
     uint64_t buffer_state_bit) {
-  return {
-      buffer_,          metadata_buffer_,           buffer_id(),
-      buffer_state_bit, acquire_fence_fd_.Borrow(), release_fence_fd_.Borrow()};
+  return {buffer_,
+          metadata_buffer_,
+          buffer_id(),
+          channel_id(),
+          buffer_state_bit,
+          acquire_fence_fd_.Borrow(),
+          release_fence_fd_.Borrow()};
 }
 
 Status<BufferDescription<BorrowedHandle>> ProducerChannel::OnGetBuffer(
diff --git a/services/vr/bufferhubd/producer_channel.h b/services/vr/bufferhubd/producer_channel.h
index 67fdf15..10a4ce7 100644
--- a/services/vr/bufferhubd/producer_channel.h
+++ b/services/vr/bufferhubd/producer_channel.h
@@ -43,6 +43,8 @@
 
   ~ProducerChannel() override;
 
+  uint64_t buffer_state() const { return buffer_state_->load(); }
+
   bool HandleMessage(Message& message) override;
   void HandleImpulse(Message& message) override;
 
diff --git a/services/vr/bufferhubd/producer_queue_channel.cpp b/services/vr/bufferhubd/producer_queue_channel.cpp
index c0c48c2..88f5508 100644
--- a/services/vr/bufferhubd/producer_queue_channel.cpp
+++ b/services/vr/bufferhubd/producer_queue_channel.cpp
@@ -76,6 +76,11 @@
           message);
       return true;
 
+    case BufferHubRPC::ProducerQueueInsertBuffer::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ProducerQueueInsertBuffer>(
+          *this, &ProducerQueueChannel::OnProducerQueueInsertBuffer, message);
+      return true;
+
     case BufferHubRPC::ProducerQueueRemoveBuffer::Opcode:
       DispatchRemoteMethod<BufferHubRPC::ProducerQueueRemoveBuffer>(
           *this, &ProducerQueueChannel::OnProducerQueueRemoveBuffer, message);
@@ -278,6 +283,81 @@
   return {{std::move(buffer_handle), slot}};
 }
 
+Status<size_t> ProducerQueueChannel::OnProducerQueueInsertBuffer(
+    pdx::Message& message, int buffer_cid) {
+  ATRACE_NAME("ProducerQueueChannel::InsertBuffer");
+  ALOGD_IF(TRACE,
+           "ProducerQueueChannel::InsertBuffer: channel_id=%d, buffer_cid=%d",
+           channel_id(), buffer_cid);
+
+  if (capacity_ >= BufferHubRPC::kMaxQueueCapacity) {
+    ALOGE("ProducerQueueChannel::InsertBuffer: reaches kMaxQueueCapacity.");
+    return ErrorStatus(E2BIG);
+  }
+  auto producer_channel = std::static_pointer_cast<ProducerChannel>(
+      service()->GetChannel(buffer_cid));
+  if (producer_channel == nullptr ||
+      producer_channel->channel_type() != BufferHubChannel::kProducerType) {
+    // Rejects the request if the requested buffer channel is invalid and/or
+    // it's not a ProducerChannel.
+    ALOGE(
+        "ProducerQueueChannel::InsertBuffer: Invalid buffer_cid=%d, "
+        "producer_buffer=0x%p, channel_type=%d.",
+        buffer_cid, producer_channel.get(),
+        producer_channel == nullptr ? -1 : producer_channel->channel_type());
+    return ErrorStatus(EINVAL);
+  }
+  if (producer_channel->GetActiveProcessId() != message.GetProcessId()) {
+    // Rejects the request if the requested buffer channel is not currently
+    // connected to the caller this is IPC request. This effectively prevents
+    // fake buffer_cid from being injected.
+    ALOGE(
+        "ProducerQueueChannel::InsertBuffer: Requested buffer channel "
+        "(buffer_cid=%d) is not connected to the calling process (pid=%d). "
+        "It's connected to a different process (pid=%d).",
+        buffer_cid, message.GetProcessId(),
+        producer_channel->GetActiveProcessId());
+    return ErrorStatus(EINVAL);
+  }
+  uint64_t buffer_state = producer_channel->buffer_state();
+  if (!BufferHubDefs::IsBufferGained(buffer_state)) {
+    // Rejects the request if the requested buffer is not in Gained state.
+    ALOGE(
+        "ProducerQueueChannel::InsertBuffer: The buffer (cid=%d, "
+        "state=0x%" PRIx64 ") is not in gained state.",
+        buffer_cid, buffer_state);
+    return ErrorStatus(EINVAL);
+  }
+
+  // Register the to-be-inserted buffer's channel_id into the first empty
+  // buffer slot.
+  size_t slot = 0;
+  for (; slot < BufferHubRPC::kMaxQueueCapacity; slot++) {
+    if (buffers_[slot].expired())
+      break;
+  }
+  if (slot == BufferHubRPC::kMaxQueueCapacity) {
+    ALOGE(
+        "ProducerQueueChannel::AllocateBuffer: Cannot find empty slot for new "
+        "buffer allocation.");
+    return ErrorStatus(E2BIG);
+  }
+
+  buffers_[slot] = producer_channel;
+  capacity_++;
+
+  // Notify each consumer channel about the new buffer.
+  for (auto* consumer_channel : consumer_channels_) {
+    ALOGD(
+        "ProducerQueueChannel::AllocateBuffer: Notified consumer with new "
+        "buffer, buffer_cid=%d",
+        buffer_cid);
+    consumer_channel->RegisterNewBuffer(producer_channel, slot);
+  }
+
+  return {slot};
+}
+
 Status<void> ProducerQueueChannel::OnProducerQueueRemoveBuffer(
     Message& /*message*/, size_t slot) {
   if (buffers_[slot].expired()) {
diff --git a/services/vr/bufferhubd/producer_queue_channel.h b/services/vr/bufferhubd/producer_queue_channel.h
index e825f47..e4fa243 100644
--- a/services/vr/bufferhubd/producer_queue_channel.h
+++ b/services/vr/bufferhubd/producer_queue_channel.h
@@ -38,8 +38,12 @@
                                  uint32_t format, uint64_t usage,
                                  size_t buffer_count);
 
-  // Detach a BufferHubProducer indicated by |slot|. Note that the buffer must
-  // be in Gain'ed state for the producer queue to detach.
+  // Inserts a BufferProducer into the queue. Note that the buffer must be in
+  // Gain'ed state for the operation to succeed.
+  pdx::Status<size_t> OnProducerQueueInsertBuffer(pdx::Message& message, int buffer_cid);
+
+  // Removes a BufferProducer indicated by |slot|. Note that the buffer must be
+  // in Gain'ed state for the operation to succeed.
   pdx::Status<void> OnProducerQueueRemoveBuffer(pdx::Message& message,
                                                 size_t slot);