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();