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,
diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp
index 6009a95..6122846 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -17,6 +17,7 @@
"bufferhubd.cpp",
"consumer_channel.cpp",
"producer_channel.cpp",
+ "detached_buffer_channel.cpp",
"consumer_queue_channel.cpp",
"producer_queue_channel.cpp",
]
diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp
index 1eb4ef9..e4e19c7 100644
--- a/services/vr/bufferhubd/buffer_hub.cpp
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -100,6 +100,41 @@
stream << std::setw(8) << info.index;
stream << std::endl;
}
+
+ if (channel->channel_type() == BufferHubChannel::kDetachedBufferType) {
+ BufferHubChannel::BufferInfo info = channel->GetBufferInfo();
+
+ stream << std::right;
+ stream << std::setw(6) << info.id;
+ stream << " ";
+ stream << std::setw(9) << "N/A";
+ stream << " ";
+ if (info.format == HAL_PIXEL_FORMAT_BLOB) {
+ std::string size = std::to_string(info.width) + " B";
+ stream << std::setw(14) << size;
+ } else {
+ std::string dimensions = std::to_string(info.width) + "x" +
+ std::to_string(info.height) + "x" +
+ std::to_string(info.layer_count);
+ stream << std::setw(14) << dimensions;
+ }
+ stream << " ";
+ stream << std::setw(6) << info.format;
+ stream << " ";
+ stream << "0x" << std::hex << std::setfill('0');
+ stream << std::setw(8) << info.usage;
+ stream << std::dec << std::setfill(' ');
+ stream << " ";
+ stream << std::setw(9) << "N/A";
+ stream << " ";
+ stream << std::hex << std::setfill(' ');
+ stream << std::setw(18) << "Detached";
+ stream << " ";
+ stream << std::setw(18) << "N/A";
+ stream << " ";
+ stream << std::setw(10) << "N/A";
+ stream << std::endl;
+ }
}
stream << std::endl;
@@ -215,6 +250,15 @@
*this, &BufferHubService::OnCreateProducerQueue, message);
return {};
+ case BufferHubRPC::ProducerBufferDetach::Opcode:
+ // In addition to the message handler in the ProducerChannel's
+ // HandleMessage method, we also need to invalid the producer channel (and
+ // all associated consumer channels). Note that this has to be done after
+ // HandleMessage returns to make sure the IPC request has went back to the
+ // client first.
+ SetChannel(channel->channel_id(), nullptr);
+ return {};
+
default:
return DefaultHandleMessage(message);
}
diff --git a/services/vr/bufferhubd/buffer_hub.h b/services/vr/bufferhubd/buffer_hub.h
index b487b0b..e04967a 100644
--- a/services/vr/bufferhubd/buffer_hub.h
+++ b/services/vr/bufferhubd/buffer_hub.h
@@ -23,6 +23,7 @@
enum ChannelType {
kProducerType,
kConsumerType,
+ kDetachedBufferType,
kProducerQueueType,
kConsumerQueueType,
};
diff --git a/services/vr/bufferhubd/detached_buffer_channel.cpp b/services/vr/bufferhubd/detached_buffer_channel.cpp
new file mode 100644
index 0000000..edb2111
--- /dev/null
+++ b/services/vr/bufferhubd/detached_buffer_channel.cpp
@@ -0,0 +1,57 @@
+#include "detached_buffer_channel.h"
+
+using android::pdx::ErrorStatus;
+using android::pdx::Message;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::Status;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+DetachedBufferChannel::DetachedBufferChannel(BufferHubService* service,
+ int buffer_id, int channel_id,
+ IonBuffer buffer,
+ IonBuffer metadata_buffer,
+ size_t user_metadata_size)
+ : BufferHubChannel(service, buffer_id, channel_id, kDetachedBufferType),
+ buffer_(std::move(buffer)),
+ metadata_buffer_(std::move(metadata_buffer)),
+ user_metadata_size_(user_metadata_size) {}
+
+BufferHubChannel::BufferInfo DetachedBufferChannel::GetBufferInfo() const {
+ return BufferInfo(buffer_id(), /*consumer_count=*/0, buffer_.width(),
+ buffer_.height(), buffer_.layer_count(), buffer_.format(),
+ buffer_.usage(), /*pending_count=*/0, /*state=*/0,
+ /*signaled_mask=*/0, /*index=*/0);
+}
+
+void DetachedBufferChannel::HandleImpulse(Message& /*message*/) {
+ ATRACE_NAME("DetachedBufferChannel::HandleImpulse");
+}
+
+bool DetachedBufferChannel::HandleMessage(Message& message) {
+ ATRACE_NAME("DetachedBufferChannel::HandleMessage");
+ switch (message.GetOp()) {
+ case BufferHubRPC::DetachedBufferPromote::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::DetachedBufferPromote>(
+ *this, &DetachedBufferChannel::OnPromote, message);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+Status<RemoteChannelHandle> DetachedBufferChannel::OnPromote(
+ Message& /*message*/) {
+ ATRACE_NAME("DetachedBufferChannel::OnPromote");
+ ALOGD_IF(TRACE, "DetachedBufferChannel::OnPromote: buffer_id=%d",
+ buffer_id());
+
+ // TODO(b/69982239): Implement the logic to promote a detached buffer.
+ return ErrorStatus(ENOSYS);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/bufferhubd/detached_buffer_channel.h b/services/vr/bufferhubd/detached_buffer_channel.h
new file mode 100644
index 0000000..7ce4aed
--- /dev/null
+++ b/services/vr/bufferhubd/detached_buffer_channel.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_DETACHED_BUFFER_CHANNEL_H_
+#define ANDROID_DVR_BUFFERHUBD_DETACHED_BUFFER_CHANNEL_H_
+
+#include "buffer_hub.h"
+
+// #include <pdx/channel_handle.h>
+// #include <pdx/file_handle.h>
+// #include <pdx/rpc/buffer_wrapper.h>
+// #include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+class DetachedBufferChannel : public BufferHubChannel {
+ public:
+ // Creates a detached buffer.
+ DetachedBufferChannel(BufferHubService* service, int buffer_id,
+ int channel_id, IonBuffer buffer,
+ IonBuffer metadata_buffer, size_t user_metadata_size);
+
+ size_t user_metadata_size() const { return user_metadata_size_; }
+
+ // Captures buffer info for use by BufferHubService::DumpState().
+ BufferInfo GetBufferInfo() const override;
+
+ bool HandleMessage(pdx::Message& message) override;
+ void HandleImpulse(pdx::Message& message) override;
+
+ private:
+ pdx::Status<pdx::RemoteChannelHandle> OnPromote(pdx::Message& message);
+
+ // Gralloc buffer handles.
+ IonBuffer buffer_;
+ IonBuffer metadata_buffer_;
+
+ // Size of user requested metadata.
+ const size_t user_metadata_size_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFERHUBD_DETACHED_BUFFER_CHANNEL_H_
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",
diff --git a/services/vr/bufferhubd/producer_channel.h b/services/vr/bufferhubd/producer_channel.h
index c5dbf0e..de9ff31 100644
--- a/services/vr/bufferhubd/producer_channel.h
+++ b/services/vr/bufferhubd/producer_channel.h
@@ -99,6 +99,7 @@
pdx::Status<BufferDescription<BorrowedHandle>> OnGetBuffer(Message& message);
pdx::Status<void> OnProducerPost(Message& message, LocalFence acquire_fence);
pdx::Status<LocalFence> OnProducerGain(Message& message);
+ pdx::Status<RemoteChannelHandle> OnProducerDetach(Message& message);
ProducerChannel(const ProducerChannel&) = delete;
void operator=(const ProducerChannel&) = delete;