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/libs/vr/libbufferhub/buffer_hub-test.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp
index b7a6099..3ce5c9f 100644
--- a/libs/vr/libbufferhub/buffer_hub-test.cpp
+++ b/libs/vr/libbufferhub/buffer_hub-test.cpp
@@ -25,7 +25,9 @@
 using android::dvr::BufferHubDefs::IsBufferAcquired;
 using android::dvr::BufferHubDefs::IsBufferReleased;
 using android::dvr::BufferProducer;
+using android::pdx::LocalChannelHandle;
 using android::pdx::LocalHandle;
+using android::pdx::Status;
 
 const int kWidth = 640;
 const int kHeight = 480;
@@ -717,3 +719,59 @@
   // Producer should be able to gain no matter what.
   EXPECT_EQ(0, p->GainAsync(&meta, &fence));
 }
+
+TEST_F(LibBufferHubTest, TestDetachBufferFromProducer) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(p.get() != nullptr);
+  ASSERT_TRUE(c.get() != nullptr);
+
+  DvrNativeBufferMetadata metadata;
+  LocalHandle invalid_fence;
+
+  // Detach in posted state should fail.
+  EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+  EXPECT_GT(RETRY_EINTR(c->Poll(kPollTimeoutMs)), 0);
+  auto s1 = p->Detach();
+  EXPECT_FALSE(s1);
+
+  // Detach in acquired state should fail.
+  EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
+  s1 = p->Detach();
+  EXPECT_FALSE(s1);
+
+  // Detach in released state should fail.
+  EXPECT_EQ(0, c->ReleaseAsync(&metadata, invalid_fence));
+  EXPECT_GT(RETRY_EINTR(p->Poll(kPollTimeoutMs)), 0);
+  s1 = p->Detach();
+  EXPECT_FALSE(s1);
+
+  // Detach in gained state should succeed.
+  EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
+  s1 = p->Detach();
+  EXPECT_TRUE(s1);
+
+  LocalChannelHandle detached_buffer = s1.take();
+  EXPECT_TRUE(detached_buffer.valid());
+
+  // Both producer and consumer should have hangup.
+  EXPECT_GT(RETRY_EINTR(p->Poll(kPollTimeoutMs)), 0);
+  auto s2 = p->GetEventMask(POLLHUP);
+  EXPECT_TRUE(s2);
+  EXPECT_EQ(s2.get(), POLLHUP);
+
+  EXPECT_GT(RETRY_EINTR(c->Poll(kPollTimeoutMs)), 0);
+  s2 = p->GetEventMask(POLLHUP);
+  EXPECT_TRUE(s2);
+  EXPECT_EQ(s2.get(), POLLHUP);
+
+  auto s3 = p->CreateConsumer();
+  EXPECT_FALSE(s3);
+  EXPECT_EQ(s3.error(), EOPNOTSUPP);
+
+  s3 = c->CreateConsumer();
+  EXPECT_FALSE(s3);
+  EXPECT_EQ(s3.error(), EOPNOTSUPP);
+}
diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp
index 6db09a9..13971eb 100644
--- a/libs/vr/libbufferhub/buffer_hub_client.cpp
+++ b/libs/vr/libbufferhub/buffer_hub_client.cpp
@@ -608,5 +608,23 @@
                        : LocalChannelHandle{nullptr, -status.error()});
 }
 
+Status<LocalChannelHandle> BufferProducer::Detach() {
+  uint64_t buffer_state = buffer_state_->load();
+  if (!BufferHubDefs::IsBufferGained(buffer_state)) {
+    // Can only detach a BufferProducer when it's in gained state.
+    ALOGW("BufferProducer::Detach: The buffer (id=%d, state=0x%" PRIx64
+          ") is not in gained state.",
+          id(), buffer_state);
+    return {};
+  }
+
+  Status<LocalChannelHandle> status =
+      InvokeRemoteMethod<BufferHubRPC::ProducerBufferDetach>();
+  ALOGE_IF(!status,
+           "BufferProducer::Detach: Failed to detach buffer (id=%d): %s.", id(),
+           status.GetErrorMessage().c_str());
+  return status;
+}
+
 }  // namespace dvr
 }  // namespace android
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 c791250..32448a1 100644
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
@@ -217,6 +217,14 @@
   // succeeded, or a negative errno code if local error check fails.
   int GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
 
+  // Detaches a ProducerBuffer from an existing producer/consumer set. Can only
+  // be called when a producer buffer has exclusive access to the buffer (i.e.
+  // in the gain'ed state). On the successful return of the IPC call, a new
+  // LocalChannelHandle representing a detached buffer will be returned and all
+  // existing producer and consumer channels will be closed. Further IPCs
+  // towards those channels will return error.
+  Status<LocalChannelHandle> Detach();
+
  private:
   friend BASE;
 
diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
index c70fffc..fabefd5 100644
--- a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
+++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
@@ -373,6 +373,10 @@
     kOpConsumerAcquire,
     kOpConsumerRelease,
     kOpConsumerSetIgnore,
+    kOpProducerBufferDetach,
+    kOpConsumerBufferDetach,
+    kOpCreateDetachedBuffer,
+    kOpDetachedBufferPromote,
     kOpCreateProducerQueue,
     kOpCreateConsumerQueue,
     kOpGetQueueInfo,
@@ -400,6 +404,28 @@
   PDX_REMOTE_METHOD(ConsumerRelease, kOpConsumerRelease,
                     void(LocalFence release_fence));
   PDX_REMOTE_METHOD(ConsumerSetIgnore, kOpConsumerSetIgnore, void(bool ignore));
+  PDX_REMOTE_METHOD(ProducerBufferDetach, kOpProducerBufferDetach,
+                    LocalChannelHandle(Void));
+
+  // Detaches a ConsumerBuffer from an existing producer/consumer set. Can only
+  // be called when the consumer is the only consumer and it has exclusive
+  // access to the buffer (i.e. in the acquired'ed state). On the successful
+  // return of the IPC call, a new DetachedBufferChannel handle will be returned
+  // and all existing producer and consumer channels will be closed. Further
+  // IPCs towards those channels will return error.
+  PDX_REMOTE_METHOD(ConsumerBufferDetach, kOpConsumerBufferDetach,
+                    LocalChannelHandle(Void));
+
+  // Creates a standalone DetachedBuffer not associated with any
+  // producer/consumer set.
+  PDX_REMOTE_METHOD(CreateDetachedBuffer, kOpCreateDetachedBuffer,
+                    LocalChannelHandle(Void));
+
+  // Promotes a DetachedBuffer to become a ProducerBuffer. Once promoted the
+  // DetachedBuffer channel will be closed automatically on successful IPC
+  // return. Further IPCs towards this channel will return error.
+  PDX_REMOTE_METHOD(DetachedBufferPromote, kOpDetachedBufferPromote,
+                    LocalChannelHandle(Void));
 
   // Buffer Queue Methods.
   PDX_REMOTE_METHOD(CreateProducerQueue, kOpCreateProducerQueue,