bufferhubd: Add DetachedBufferChannel

1/ Introduces four new BufferHub RPC operations.
2/ Implements the ProducerBufferDetach with test case.
3/ Also fixes a bug that Hangup signal wasn't propogated to the client
   when bufferhubd closes a producer channel.

Bug: 38137191
Bug: 70046255
Bug: 70912269
Test: buffer_hub-test
Change-Id: Ia2ba0e95abd3c1b960670c505c6fdb9c9de3a6dd
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
index 8160193..e141b91 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -13,6 +13,7 @@
 
 #include <private/dvr/bufferhub_rpc.h>
 #include "consumer_channel.h"
+#include "detached_buffer_channel.h"
 
 using android::pdx::BorrowedHandle;
 using android::pdx::ErrorStatus;
@@ -131,8 +132,11 @@
            "ProducerChannel::~ProducerChannel: channel_id=%d buffer_id=%d "
            "state=%" PRIx64 ".",
            channel_id(), buffer_id(), buffer_state_->load());
-  for (auto consumer : consumer_channels_)
+  for (auto consumer : consumer_channels_) {
     consumer->OnProducerClosed();
+    service()->SetChannel(consumer->channel_id(), nullptr);
+  }
+  Hangup();
 }
 
 BufferHubChannel::BufferInfo ProducerChannel::GetBufferInfo() const {
@@ -183,6 +187,11 @@
           *this, &ProducerChannel::OnProducerGain, message);
       return true;
 
+    case BufferHubRPC::ProducerBufferDetach::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ProducerBufferDetach>(
+          *this, &ProducerChannel::OnProducerDetach, message);
+      return true;
+
     default:
       return false;
   }
@@ -337,6 +346,55 @@
   return {std::move(returned_fence_)};
 }
 
+Status<RemoteChannelHandle> ProducerChannel::OnProducerDetach(
+    Message& message) {
+  ATRACE_NAME("ProducerChannel::OnProducerDetach");
+  ALOGD_IF(TRACE, "ProducerChannel::OnProducerDetach: buffer_id=%d",
+           buffer_id());
+
+  uint64_t buffer_state = buffer_state_->load();
+  if (!BufferHubDefs::IsBufferGained(buffer_state)) {
+    // Can only detach a BufferProducer when it's in gained state.
+    ALOGW(
+        "ProducerChannel::OnProducerDetach: The buffer (id=%d, state=0x%" PRIx64
+        ") is not in gained state.",
+        buffer_id(), buffer_state);
+    return {};
+  }
+
+  int channel_id;
+  auto status = message.PushChannel(0, nullptr, &channel_id);
+  if (!status) {
+    ALOGE(
+        "ProducerChannel::OnProducerDetach: Failed to push detached buffer "
+        "channel: %s",
+        status.GetErrorMessage().c_str());
+    return ErrorStatus(ENOMEM);
+  }
+
+  // Make sure we unlock the buffer.
+  if (int ret = metadata_buffer_.Unlock()) {
+    ALOGE("ProducerChannel::OnProducerDetach: Failed to unlock metadata.");
+    return ErrorStatus(-ret);
+  };
+
+  auto channel = std::make_shared<DetachedBufferChannel>(
+      service(), buffer_id(), channel_id, std::move(buffer_),
+      std::move(metadata_buffer_), user_metadata_size_);
+
+  const auto channel_status = service()->SetChannel(channel_id, channel);
+  if (!channel_status) {
+    // Technically, this should never fail, as we just pushed the channel. Note
+    // that LOG_FATAL will be stripped out in non-debug build.
+    LOG_FATAL(
+        "ProducerChannel::OnProducerDetach: Failed to set new detached buffer "
+        "channel: %s.",
+        channel_status.GetErrorMessage().c_str());
+  }
+
+  return status;
+}
+
 Status<LocalFence> ProducerChannel::OnConsumerAcquire(Message& /*message*/) {
   ATRACE_NAME("ProducerChannel::OnConsumerAcquire");
   ALOGD_IF(TRACE, "ProducerChannel::OnConsumerAcquire: buffer_id=%d",