Add DaydreamVR native libraries and services
Upstreaming the main VR system components from master-dreamos-dev
into goog/master.
Bug: None
Test: `m -j32` succeeds. Sailfish boots and basic_vr sample app works
Change-Id: I853015872afc443aecee10411ef2d6b79184d051
diff --git a/services/vr/bufferhubd/Android.mk b/services/vr/bufferhubd/Android.mk
new file mode 100644
index 0000000..492acb2
--- /dev/null
+++ b/services/vr/bufferhubd/Android.mk
@@ -0,0 +1,51 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+ buffer_hub.cpp \
+ bufferhubd.cpp \
+ consumer_channel.cpp \
+ producer_channel.cpp \
+ consumer_queue_channel.cpp \
+ producer_queue_channel.cpp \
+
+staticLibraries := \
+ libchrome \
+ libperformance \
+ libpdx_default_transport \
+ libbufferhub
+
+sharedLibraries := \
+ libbase \
+ libcutils \
+ libhardware \
+ liblog \
+ libsync \
+ libutils
+
+include $(CLEAR_VARS)
+# Don't strip symbols so we see stack traces in logcat.
+LOCAL_STRIP_MODULE := false
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_CFLAGS := -DLOG_TAG=\"bufferhubd\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_GRAPHICS
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := bufferhubd
+LOCAL_INIT_RC := bufferhubd.rc
+include $(BUILD_EXECUTABLE)
+
diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp
new file mode 100644
index 0000000..a0c7439
--- /dev/null
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -0,0 +1,474 @@
+#include "buffer_hub.h"
+
+#include <cutils/log.h>
+#include <poll.h>
+#include <utils/Trace.h>
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include "consumer_channel.h"
+#include "producer_channel.h"
+#include "producer_queue_channel.h"
+
+using android::pdx::Channel;
+using android::pdx::Message;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::default_transport::Endpoint;
+
+namespace android {
+namespace dvr {
+
+BufferHubService::BufferHubService()
+ : BASE("BufferHub", Endpoint::Create(BufferHubRPC::kClientPath)) {}
+
+BufferHubService::~BufferHubService() {}
+
+bool BufferHubService::IsInitialized() const {
+ return BASE::IsInitialized() && IonBuffer::GetGrallocModule();
+}
+
+std::string BufferHubService::DumpState(size_t /*max_length*/) {
+ std::ostringstream stream;
+ auto channels = GetChannels<BufferHubChannel>();
+
+ std::sort(channels.begin(), channels.end(),
+ [](const std::shared_ptr<BufferHubChannel>& a,
+ const std::shared_ptr<BufferHubChannel>& b) {
+ return a->buffer_id() < b->buffer_id();
+ });
+
+ stream << "Active Producer Buffers:\n";
+ stream << std::right;
+ stream << std::setw(6) << "Id";
+ stream << " ";
+ stream << std::setw(9) << "Consumers";
+ stream << " ";
+ stream << std::setw(14) << "Geometry";
+ stream << " ";
+ stream << std::setw(6) << "Format";
+ stream << " ";
+ stream << std::setw(10) << "Usage";
+ stream << " ";
+ stream << "Name";
+ stream << std::endl;
+
+ for (const auto& channel : channels) {
+ if (channel->channel_type() == BufferHubChannel::kProducerType) {
+ BufferHubChannel::BufferInfo info = channel->GetBufferInfo();
+
+ stream << std::right;
+ stream << std::setw(6) << info.id;
+ stream << " ";
+ stream << std::setw(9) << info.consumer_count;
+ 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.slice_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 << info.name;
+ stream << std::endl;
+ }
+ }
+
+ stream << "Active Consumer Buffers:\n";
+ stream << std::right;
+ stream << std::setw(6) << "Id";
+ stream << " ";
+ stream << std::setw(14) << "Geometry";
+ stream << " ";
+ stream << "Name";
+ stream << std::endl;
+
+ for (const auto& channel : channels) {
+ if (channel->channel_type() == BufferHubChannel::kConsumerType) {
+ BufferHubChannel::BufferInfo info = channel->GetBufferInfo();
+
+ stream << std::right;
+ stream << std::setw(6) << info.id;
+ stream << " ";
+
+ if (info.consumer_count == 0) {
+ // consumer_count is tracked by producer. When it's zero, producer must
+ // have already hung up and the consumer is orphaned.
+ stream << std::setw(14) << "Orphaned.";
+ stream << std::endl;
+ continue;
+ }
+
+ 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.slice_count);
+ stream << std::setw(14) << dimensions;
+ }
+ stream << " ";
+ stream << info.name;
+ stream << std::endl;
+ }
+ }
+
+ stream << std::endl;
+ stream << "Active Producer Queues:\n";
+ stream << std::right << std::setw(6) << "Id";
+ stream << std::right << std::setw(12) << " Allocated";
+ stream << std::right << std::setw(12) << " Consumers";
+ stream << " UsageSetMask";
+ stream << " UsageClearMask";
+ stream << " UsageDenySetMask";
+ stream << " UsageDenyClearMask";
+ stream << " ";
+ stream << std::endl;
+
+ for (const auto& channel : channels) {
+ if (channel->channel_type() == BufferHubChannel::kProducerQueueType) {
+ BufferHubChannel::BufferInfo info = channel->GetBufferInfo();
+
+ stream << std::dec << std::setfill(' ');
+ stream << std::right << std::setw(6) << info.id;
+ stream << std::right << std::setw(12) << info.capacity;
+ stream << std::right << std::setw(12) << info.consumer_count;
+ stream << std::setw(5) << std::setfill(' ') << "0x";
+ stream << std::hex << std::setfill('0');
+ stream << std::setw(8) << info.usage_set_mask;
+ stream << std::setw(7) << std::setfill(' ') << "0x";
+ stream << std::hex << std::setfill('0');
+ stream << std::setw(8) << info.usage_clear_mask;
+ stream << std::setw(9) << std::setfill(' ') << "0x";
+ stream << std::hex << std::setfill('0');
+ stream << std::setw(8) << info.usage_deny_set_mask;
+ stream << std::setw(11) << std::setfill(' ') << "0x";
+ stream << std::hex << std::setfill('0');
+ stream << std::setw(8) << info.usage_deny_clear_mask;
+ }
+ }
+
+ stream << std::endl;
+ stream << "Active Consumer Queues:\n";
+ stream << std::dec << std::setfill(' ');
+ stream << std::right << std::setw(6) << "Id";
+ stream << std::right << std::setw(12) << " Imported";
+ stream << " ";
+ stream << std::endl;
+
+ for (const auto& channel : channels) {
+ if (channel->channel_type() == BufferHubChannel::kConsumerQueueType) {
+ BufferHubChannel::BufferInfo info = channel->GetBufferInfo();
+
+ stream << std::right << std::setw(6) << info.id;
+ stream << std::right << std::setw(12) << info.capacity;
+ }
+ }
+
+ return stream.str();
+}
+
+void BufferHubService::HandleImpulse(Message& message) {
+ ATRACE_NAME("BufferHubService::HandleImpulse");
+ if (auto channel = message.GetChannel<BufferHubChannel>())
+ channel->HandleImpulse(message);
+}
+
+int BufferHubService::HandleMessage(Message& message) {
+ ATRACE_NAME("BufferHubService::HandleMessage");
+ auto channel = message.GetChannel<BufferHubChannel>();
+
+ ALOGD_IF(
+ TRACE,
+ "BufferHubService::HandleMessage: channel=%p channel_id=%d opcode=%d",
+ channel.get(), message.GetChannelId(), message.GetOp());
+
+ // If the channel is already set up, let it handle the message.
+ if (channel && !channel->HandleMessage(message))
+ return DefaultHandleMessage(message);
+
+ // This channel has not been set up yet, the following are valid operations.
+ switch (message.GetOp()) {
+ case BufferHubRPC::CreateBuffer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::CreateBuffer>(
+ *this, &BufferHubService::OnCreateBuffer, message);
+ return 0;
+
+ case BufferHubRPC::CreatePersistentBuffer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
+ *this, &BufferHubService::OnCreatePersistentBuffer, message);
+ return 0;
+
+ case BufferHubRPC::GetPersistentBuffer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::GetPersistentBuffer>(
+ *this, &BufferHubService::OnGetPersistentBuffer, message);
+ return 0;
+
+ case BufferHubRPC::CreateProducerQueue::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::CreateProducerQueue>(
+ *this, &BufferHubService::OnCreateProducerQueue, message);
+ return 0;
+
+ default:
+ return DefaultHandleMessage(message);
+ }
+}
+
+void BufferHubService::OnChannelClose(Message&,
+ const std::shared_ptr<Channel>& channel) {
+ if (auto buffer = std::static_pointer_cast<BufferHubChannel>(channel))
+ buffer->Detach();
+}
+
+int BufferHubService::OnCreateBuffer(Message& message, int width, int height,
+ int format, int usage,
+ size_t meta_size_bytes,
+ size_t slice_count) {
+ // Use the producer channel id as the global buffer id.
+ const int buffer_id = message.GetChannelId();
+ ALOGD_IF(TRACE,
+ "BufferHubService::OnCreateBuffer: buffer_id=%d width=%d height=%d "
+ "format=%d usage=%d meta_size_bytes=%zu slice_count=%zu",
+ buffer_id, width, height, format, usage, meta_size_bytes,
+ slice_count);
+
+ // See if this channel is already attached to a buffer.
+ if (const auto channel = message.GetChannel<BufferHubChannel>()) {
+ ALOGE("BufferHubService::OnCreateBuffer: Buffer already created: buffer=%d",
+ buffer_id);
+ return -EALREADY;
+ }
+
+ int error;
+ if (const auto producer_channel =
+ ProducerChannel::Create(this, buffer_id, width, height, format, usage,
+ meta_size_bytes, slice_count, &error)) {
+ message.SetChannel(producer_channel);
+ return 0;
+ } else {
+ ALOGE("BufferHubService::OnCreateBuffer: Failed to create producer!!");
+ return error;
+ }
+}
+
+int BufferHubService::OnCreatePersistentBuffer(
+ Message& message, const std::string& name, int user_id, int group_id,
+ int width, int height, int format, int usage, size_t meta_size_bytes,
+ size_t slice_count) {
+ const int channel_id = message.GetChannelId();
+ ALOGD_IF(TRACE,
+ "BufferHubService::OnCreatePersistentBuffer: channel_id=%d name=%s "
+ "user_id=%d group_id=%d width=%d height=%d format=%d usage=%d "
+ "meta_size_bytes=%zu slice_count=%zu",
+ channel_id, name.c_str(), user_id, group_id, width, height, format,
+ usage, meta_size_bytes, slice_count);
+
+ // See if this channel is already attached to a buffer.
+ if (const auto channel = message.GetChannel<BufferHubChannel>()) {
+ ALOGE(
+ "BufferHubService::OnCreatePersistentBuffer: Channel already attached "
+ "to buffer: channel_id=%d buffer_id=%d",
+ channel_id, channel->buffer_id());
+ return -EALREADY;
+ }
+
+ const int euid = message.GetEffectiveUserId();
+ const int egid = message.GetEffectiveGroupId();
+ int error;
+
+ if (auto buffer = GetNamedBuffer(name)) {
+ if (!buffer->CheckAccess(euid, egid)) {
+ ALOGE(
+ "BufferHubService::OnCreatePersistentBuffer: Requesting process does "
+ "not have permission to access named buffer: name=%s euid=%d egid=%d",
+ name.c_str(), euid, euid);
+ return -EPERM;
+ } else if (!buffer->CheckParameters(width, height, format, usage,
+ meta_size_bytes, slice_count)) {
+ ALOGE(
+ "BufferHubService::OnCreatePersistentBuffer: Requested an existing "
+ "buffer with different parameters: name=%s",
+ name.c_str());
+ return -EINVAL;
+ } else if (!buffer->IsDetached()) {
+ ALOGE(
+ "BufferHubService::OnCreatePersistentBuffer: Requesting a persistent "
+ "buffer that is already attached to a channel: name=%s",
+ name.c_str());
+ return -EINVAL;
+ } else {
+ buffer->Attach(channel_id);
+ message.SetChannel(buffer);
+ return 0;
+ }
+ } else if (auto buffer = ProducerChannel::Create(
+ this, channel_id, width, height, format, usage,
+ meta_size_bytes, slice_count, &error)) {
+ const int ret =
+ buffer->OnProducerMakePersistent(message, name, user_id, group_id);
+ if (!ret)
+ message.SetChannel(buffer);
+ return ret;
+ } else {
+ ALOGE("BufferHubService::OnCreateBuffer: Failed to create producer!!");
+ return error;
+ }
+}
+
+int BufferHubService::OnGetPersistentBuffer(Message& message,
+ const std::string& name) {
+ const int channel_id = message.GetChannelId();
+ ALOGD_IF(TRACE,
+ "BufferHubService::OnGetPersistentBuffer: channel_id=%d name=%s",
+ channel_id, name.c_str());
+
+ // See if this channel is already attached to a buffer.
+ if (const auto channel = message.GetChannel<BufferHubChannel>()) {
+ ALOGE(
+ "BufferHubService::OnGetPersistentBuffer: Channel already attached to "
+ "buffer: channel_id=%d buffer_id=%d",
+ channel_id, channel->buffer_id());
+ return -EALREADY;
+ }
+
+ const int euid = message.GetEffectiveUserId();
+ const int egid = message.GetEffectiveGroupId();
+
+ if (auto buffer = GetNamedBuffer(name)) {
+ if (!buffer->CheckAccess(euid, egid)) {
+ ALOGE(
+ "BufferHubService::OnGetPersistentBuffer: Requesting process does "
+ "not have permission to access named buffer: name=%s euid=%d egid=%d",
+ name.c_str(), euid, egid);
+ return -EPERM;
+ } else if (!buffer->IsDetached()) {
+ ALOGE(
+ "BufferHubService::OnGetPersistentBuffer: Requesting a persistent "
+ "buffer that is already attached to a channel: name=%s",
+ name.c_str());
+ return -EINVAL;
+ } else {
+ buffer->Attach(channel_id);
+ message.SetChannel(buffer);
+ return 0;
+ }
+ } else {
+ ALOGE("BufferHubService::OnGetPersistentBuffer: Buffer \"%s\" not found!",
+ name.c_str());
+ return -ENOENT;
+ }
+}
+
+int BufferHubService::OnCreateProducerQueue(
+ pdx::Message& message, size_t meta_size_bytes, int usage_set_mask,
+ int usage_clear_mask, int usage_deny_set_mask, int usage_deny_clear_mask) {
+ // Use the producer channel id as the global queue id.
+ const int queue_id = message.GetChannelId();
+ ALOGD_IF(TRACE, "BufferHubService::OnCreateProducerQueue: queue_id=%d",
+ queue_id);
+
+ // See if this channel is already attached to another object.
+ if (const auto channel = message.GetChannel<BufferHubChannel>()) {
+ ALOGE("BufferHubService::OnCreateProducerQueue: already created: queue=%d",
+ queue_id);
+ return -EALREADY;
+ }
+
+ int error;
+ if (const auto producer_channel = ProducerQueueChannel::Create(
+ this, queue_id, meta_size_bytes, usage_set_mask, usage_clear_mask,
+ usage_deny_set_mask, usage_deny_clear_mask, &error)) {
+ message.SetChannel(producer_channel);
+ return 0;
+ } else {
+ ALOGE("BufferHubService::OnCreateBuffer: Failed to create producer!!");
+ return error;
+ }
+}
+
+bool BufferHubService::AddNamedBuffer(
+ const std::string& name, const std::shared_ptr<ProducerChannel>& buffer) {
+ auto search = named_buffers_.find(name);
+ if (search == named_buffers_.end()) {
+ named_buffers_.emplace(name, buffer);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+std::shared_ptr<ProducerChannel> BufferHubService::GetNamedBuffer(
+ const std::string& name) {
+ auto search = named_buffers_.find(name);
+ if (search != named_buffers_.end())
+ return search->second;
+ else
+ return nullptr;
+}
+
+bool BufferHubService::RemoveNamedBuffer(const ProducerChannel& buffer) {
+ for (auto it = named_buffers_.begin(); it != named_buffers_.end();) {
+ if (it->second.get() == &buffer) {
+ named_buffers_.erase(it);
+ return true;
+ }
+ ++it;
+ }
+ return false;
+}
+
+void BufferHubChannel::SignalAvailable() {
+ ATRACE_NAME("BufferHubChannel::SignalAvailable");
+ if (!IsDetached()) {
+ const int ret = service_->ModifyChannelEvents(channel_id_, 0, POLLIN);
+ ALOGE_IF(ret < 0,
+ "BufferHubChannel::SignalAvailable: failed to signal availability "
+ "channel_id=%d: %s",
+ channel_id_, strerror(-ret));
+ } else {
+ ALOGD_IF(TRACE, "BufferHubChannel::SignalAvailable: detached buffer.");
+ }
+}
+
+void BufferHubChannel::ClearAvailable() {
+ ATRACE_NAME("BufferHubChannel::ClearAvailable");
+ if (!IsDetached()) {
+ const int ret = service_->ModifyChannelEvents(channel_id_, POLLIN, 0);
+ ALOGE_IF(ret < 0,
+ "BufferHubChannel::ClearAvailable: failed to clear availability "
+ "channel_id=%d: %s",
+ channel_id_, strerror(-ret));
+ } else {
+ ALOGD_IF(TRACE, "BufferHubChannel::ClearAvailable: detached buffer.");
+ }
+}
+
+void BufferHubChannel::Hangup() {
+ ATRACE_NAME("BufferHubChannel::Hangup");
+ if (!IsDetached()) {
+ const int ret = service_->ModifyChannelEvents(channel_id_, 0, POLLHUP);
+ ALOGE_IF(
+ ret < 0,
+ "BufferHubChannel::Hangup: failed to signal hangup channel_id=%d: %s",
+ channel_id_, strerror(-ret));
+ } else {
+ ALOGD_IF(TRACE, "BufferHubChannel::Hangup: detached buffer.");
+ }
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/bufferhubd/buffer_hub.h b/services/vr/bufferhubd/buffer_hub.h
new file mode 100644
index 0000000..28cb468
--- /dev/null
+++ b/services/vr/bufferhubd/buffer_hub.h
@@ -0,0 +1,182 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_BUFFER_HUB_H_
+#define ANDROID_DVR_BUFFERHUBD_BUFFER_HUB_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include <hardware/gralloc.h>
+#include <pdx/service.h>
+
+namespace android {
+namespace dvr {
+
+class BufferHubService;
+class ConsumerChannel;
+class ProducerChannel;
+class ConsumerQueueChannel;
+class ProducerQueueChannel;
+
+class BufferHubChannel : public pdx::Channel {
+ public:
+ enum ChannelType {
+ kProducerType,
+ kConsumerType,
+ kProducerQueueType,
+ kConsumerQueueType,
+ };
+
+ enum : int { kDetachedId = -1 };
+
+ BufferHubChannel(BufferHubService* service, int buffer_id, int channel_id,
+ ChannelType channel_type)
+ : service_(service),
+ buffer_id_(buffer_id),
+ channel_id_(channel_id),
+ channel_type_(channel_type) {}
+ virtual ~BufferHubChannel() {}
+
+ virtual bool HandleMessage(pdx::Message& message) = 0;
+ virtual void HandleImpulse(pdx::Message& message) = 0;
+
+ // Captures buffer info for use by BufferHubService::DumpState().
+ struct BufferInfo {
+ // Common data field shared by BufferProducer and ProducerQueue.
+ int id = -1;
+ int type = -1;
+ size_t consumer_count = 0;
+
+ // Data field for buffer producer.
+ int width = 0;
+ int height = 0;
+ int format = 0;
+ int usage = 0;
+ size_t slice_count = 0;
+ std::string name;
+
+ // Data filed for producer queue.
+ size_t capacity = 0;
+ int usage_set_mask = 0;
+ int usage_clear_mask = 0;
+ int usage_deny_set_mask = 0;
+ int usage_deny_clear_mask = 0;
+
+ BufferInfo(int id, size_t consumer_count, int width, int height, int format,
+ int usage, size_t slice_count, const std::string& name)
+ : id(id),
+ type(kProducerType),
+ consumer_count(consumer_count),
+ width(width),
+ height(height),
+ format(format),
+ usage(usage),
+ slice_count(slice_count),
+ name(name) {}
+
+ BufferInfo(int id, size_t consumer_count, size_t capacity, int usage_set_mask,
+ int usage_clear_mask, int usage_deny_set_mask,
+ int usage_deny_clear_mask)
+ : id(id),
+ type(kProducerQueueType),
+ consumer_count(consumer_count),
+ capacity(capacity),
+ usage_set_mask(usage_set_mask),
+ usage_clear_mask(usage_clear_mask),
+ usage_deny_set_mask(usage_deny_set_mask),
+ usage_deny_clear_mask(usage_deny_clear_mask) {}
+
+ BufferInfo() {}
+ };
+
+ // Returns the buffer info for this buffer.
+ virtual BufferInfo GetBufferInfo() const = 0;
+
+ // Signal the client fd that an ownership change occurred using POLLIN.
+ void SignalAvailable();
+
+ // Clear the ownership change event.
+ void ClearAvailable();
+
+ // Signal hangup event.
+ void Hangup();
+
+ BufferHubService* service() const { return service_; }
+ ChannelType channel_type() const { return channel_type_; }
+ int buffer_id() const { return buffer_id_; }
+
+ int channel_id() const { return channel_id_; }
+ bool IsDetached() const { return channel_id_ == kDetachedId; }
+
+ void Detach() {
+ if (channel_type_ == kProducerType)
+ channel_id_ = kDetachedId;
+ }
+ void Attach(int channel_id) {
+ if (channel_type_ == kProducerType && channel_id_ == kDetachedId)
+ channel_id_ = channel_id;
+ }
+
+ private:
+ BufferHubService* service_;
+
+ // Static id of the buffer for logging and informational purposes. This id
+ // does not change for the life of the buffer.
+ // TODO(eieio): Consider using an id allocator instead of the originating
+ // channel id; channel ids wrap after 2^31 ids, but this is not a problem in
+ // general because channel ids are not used for any lookup in this service.
+ int buffer_id_;
+
+ // The channel id of the buffer. This may change for a persistent producer
+ // buffer if it is detached and re-attached to another channel.
+ int channel_id_;
+
+ ChannelType channel_type_;
+
+ BufferHubChannel(const BufferHubChannel&) = delete;
+ void operator=(const BufferHubChannel&) = delete;
+};
+
+class BufferHubService : public pdx::ServiceBase<BufferHubService> {
+ public:
+ BufferHubService();
+ ~BufferHubService() override;
+
+ int HandleMessage(pdx::Message& message) override;
+ void HandleImpulse(pdx::Message& message) override;
+
+ void OnChannelClose(pdx::Message& message,
+ const std::shared_ptr<pdx::Channel>& channel) override;
+
+ bool IsInitialized() const override;
+ std::string DumpState(size_t max_length) override;
+
+ bool AddNamedBuffer(const std::string& name,
+ const std::shared_ptr<ProducerChannel>& buffer);
+ std::shared_ptr<ProducerChannel> GetNamedBuffer(const std::string& name);
+ bool RemoveNamedBuffer(const ProducerChannel& buffer);
+
+ private:
+ friend BASE;
+
+ std::unordered_map<std::string, std::shared_ptr<ProducerChannel>>
+ named_buffers_;
+
+ int OnCreateBuffer(pdx::Message& message, int width, int height, int format,
+ int usage, size_t meta_size_bytes, size_t slice_count);
+ int OnCreatePersistentBuffer(pdx::Message& message, const std::string& name,
+ int user_id, int group_id, int width, int height,
+ int format, int usage, size_t meta_size_bytes,
+ size_t slice_count);
+ int OnGetPersistentBuffer(pdx::Message& message, const std::string& name);
+ int OnCreateProducerQueue(pdx::Message& message, size_t meta_size_bytes,
+ int usage_set_mask, int usage_clear_mask,
+ int usage_deny_set_mask, int usage_deny_clear_mask);
+
+ BufferHubService(const BufferHubService&) = delete;
+ void operator=(const BufferHubService&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFERHUBD_BUFFER_HUB_H_
diff --git a/services/vr/bufferhubd/bufferhubd.cpp b/services/vr/bufferhubd/bufferhubd.cpp
new file mode 100644
index 0000000..a8e2ddf
--- /dev/null
+++ b/services/vr/bufferhubd/bufferhubd.cpp
@@ -0,0 +1,37 @@
+#include <sched.h>
+#include <unistd.h>
+
+#include <cutils/log.h>
+
+#include <dvr/performance_client_api.h>
+#include <pdx/default_transport/service_dispatcher.h>
+
+#include "buffer_hub.h"
+
+int main(int, char**) {
+ int ret = -1;
+ std::shared_ptr<android::pdx::Service> service;
+ std::unique_ptr<android::pdx::ServiceDispatcher> dispatcher;
+
+ // We need to be able to create endpoints with full perms.
+ umask(0000);
+
+ dispatcher = android::pdx::default_transport::ServiceDispatcher::Create();
+ CHECK_ERROR(!dispatcher, error, "Failed to create service dispatcher\n");
+
+ service = android::dvr::BufferHubService::Create();
+ CHECK_ERROR(!service, error, "Failed to create buffer hub service\n");
+ dispatcher->AddService(service);
+
+ ret = dvrSetSchedulerClass(0, "graphics");
+ CHECK_ERROR(ret < 0, error, "Failed to set thread priority");
+
+ ALOGI("Entering message loop.");
+
+ ret = dispatcher->EnterDispatchLoop();
+ CHECK_ERROR(ret < 0, error, "Dispatch loop exited because: %s\n",
+ strerror(-ret));
+
+error:
+ return -ret;
+}
diff --git a/services/vr/bufferhubd/bufferhubd.rc b/services/vr/bufferhubd/bufferhubd.rc
new file mode 100644
index 0000000..ceedf1a
--- /dev/null
+++ b/services/vr/bufferhubd/bufferhubd.rc
@@ -0,0 +1,6 @@
+service bufferhubd /system/bin/bufferhubd
+ class core
+ user system
+ group system
+ cpuset /
+
diff --git a/services/vr/bufferhubd/consumer_channel.cpp b/services/vr/bufferhubd/consumer_channel.cpp
new file mode 100644
index 0000000..8db92a3
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_channel.cpp
@@ -0,0 +1,182 @@
+#include "consumer_channel.h"
+
+#include <cutils/log.h>
+#include <utils/Trace.h>
+
+#include <thread>
+
+#include <private/dvr/bufferhub_rpc.h>
+#include "producer_channel.h"
+
+using android::pdx::BorrowedHandle;
+using android::pdx::Channel;
+using android::pdx::Message;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+ConsumerChannel::ConsumerChannel(BufferHubService* service, int buffer_id,
+ int channel_id,
+ const std::shared_ptr<Channel> producer)
+ : BufferHubChannel(service, buffer_id, channel_id, kConsumerType),
+ handled_(true),
+ ignored_(false),
+ producer_(producer) {
+ GetProducer()->AddConsumer(this);
+}
+
+ConsumerChannel::~ConsumerChannel() {
+ ALOGD_IF(TRACE, "ConsumerChannel::~ConsumerChannel: channel_id=%d",
+ channel_id());
+
+ if (auto producer = GetProducer()) {
+ if (!handled_) // Producer is waiting for our Release.
+ producer->OnConsumerIgnored();
+ producer->RemoveConsumer(this);
+ }
+}
+
+BufferHubChannel::BufferInfo ConsumerChannel::GetBufferInfo() const {
+ BufferHubChannel::BufferInfo info;
+ if (auto producer = GetProducer()) {
+ // If producer has not hung up, copy most buffer info from the producer.
+ info = producer->GetBufferInfo();
+ }
+ info.id = buffer_id();
+ return info;
+}
+
+std::shared_ptr<ProducerChannel> ConsumerChannel::GetProducer() const {
+ return std::static_pointer_cast<ProducerChannel>(producer_.lock());
+}
+
+void ConsumerChannel::HandleImpulse(Message& message) {
+ ATRACE_NAME("ConsumerChannel::HandleImpulse");
+ switch (message.GetOp()) {
+ case BufferHubRPC::ConsumerRelease::Opcode:
+ OnConsumerRelease(message, {});
+ break;
+ }
+}
+
+bool ConsumerChannel::HandleMessage(Message& message) {
+ ATRACE_NAME("ConsumerChannel::HandleMessage");
+ auto producer = GetProducer();
+ if (!producer)
+ REPLY_ERROR_RETURN(message, EPIPE, true);
+
+ switch (message.GetOp()) {
+ case BufferHubRPC::GetBuffer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::GetBuffer>(
+ *producer, &ProducerChannel::OnGetBuffer, message);
+ return true;
+
+ case BufferHubRPC::GetBuffers::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::GetBuffers>(
+ *producer, &ProducerChannel::OnGetBuffers, message);
+ return true;
+
+ case BufferHubRPC::NewConsumer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::NewConsumer>(
+ *producer, &ProducerChannel::OnNewConsumer, message);
+ return true;
+
+ case BufferHubRPC::ConsumerAcquire::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ConsumerAcquire>(
+ *this, &ConsumerChannel::OnConsumerAcquire, message);
+ return true;
+
+ case BufferHubRPC::ConsumerRelease::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ConsumerRelease>(
+ *this, &ConsumerChannel::OnConsumerRelease, message);
+ return true;
+
+ case BufferHubRPC::ConsumerSetIgnore::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(
+ *this, &ConsumerChannel::OnConsumerSetIgnore, message);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+std::pair<BorrowedFence, ConsumerChannel::MetaData>
+ConsumerChannel::OnConsumerAcquire(Message& message,
+ std::size_t metadata_size) {
+ ATRACE_NAME("ConsumerChannel::OnConsumerAcquire");
+ auto producer = GetProducer();
+ if (!producer)
+ REPLY_ERROR_RETURN(message, EPIPE, {});
+
+ if (ignored_ || handled_) {
+ ALOGE(
+ "ConsumerChannel::OnConsumerAcquire: Acquire when not posted: "
+ "ignored=%d handled=%d channel_id=%d buffer_id=%d",
+ ignored_, handled_, message.GetChannelId(), producer->buffer_id());
+ REPLY_ERROR_RETURN(message, EBUSY, {});
+ } else {
+ ClearAvailable();
+ return producer->OnConsumerAcquire(message, metadata_size);
+ }
+}
+
+int ConsumerChannel::OnConsumerRelease(Message& message,
+ LocalFence release_fence) {
+ ATRACE_NAME("ConsumerChannel::OnConsumerRelease");
+ auto producer = GetProducer();
+ if (!producer)
+ return -EPIPE;
+
+ if (ignored_ || handled_) {
+ ALOGE(
+ "ConsumerChannel::OnConsumerRelease: Release when not acquired: "
+ "ignored=%d handled=%d channel_id=%d buffer_id=%d",
+ ignored_, handled_, message.GetChannelId(), producer->buffer_id());
+ return -EBUSY;
+ } else {
+ ClearAvailable();
+ const int ret =
+ producer->OnConsumerRelease(message, std::move(release_fence));
+ handled_ = ret == 0;
+ return ret;
+ }
+}
+
+int ConsumerChannel::OnConsumerSetIgnore(Message&, bool ignored) {
+ ATRACE_NAME("ConsumerChannel::OnConsumerSetIgnore");
+ auto producer = GetProducer();
+ if (!producer)
+ return -EPIPE;
+
+ ignored_ = ignored;
+ if (ignored_ && !handled_) {
+ // Update the producer if ignore is set after the consumer acquires the
+ // buffer.
+ ClearAvailable();
+ producer->OnConsumerIgnored();
+ handled_ = false;
+ }
+
+ return 0;
+}
+
+bool ConsumerChannel::OnProducerPosted() {
+ if (ignored_) {
+ handled_ = true;
+ return false;
+ } else {
+ handled_ = false;
+ SignalAvailable();
+ return true;
+ }
+}
+
+void ConsumerChannel::OnProducerClosed() {
+ producer_.reset();
+ Hangup();
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/bufferhubd/consumer_channel.h b/services/vr/bufferhubd/consumer_channel.h
new file mode 100644
index 0000000..d2a078f
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_channel.h
@@ -0,0 +1,51 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_CONSUMER_CHANNEL_H_
+#define ANDROID_DVR_BUFFERHUBD_CONSUMER_CHANNEL_H_
+
+#include "buffer_hub.h"
+
+#include <pdx/rpc/buffer_wrapper.h>
+#include <private/dvr/bufferhub_rpc.h>
+
+namespace android {
+namespace dvr {
+
+// Consumer channels are attached to a Producer channel
+class ConsumerChannel : public BufferHubChannel {
+ public:
+ using Channel = pdx::Channel;
+ using Message = pdx::Message;
+
+ ConsumerChannel(BufferHubService* service, int buffer_id, int channel_id,
+ const std::shared_ptr<Channel> producer);
+ ~ConsumerChannel() override;
+
+ bool HandleMessage(Message& message) override;
+ void HandleImpulse(Message& message) override;
+
+ BufferInfo GetBufferInfo() const override;
+
+ bool OnProducerPosted();
+ void OnProducerClosed();
+
+ private:
+ using MetaData = pdx::rpc::BufferWrapper<std::uint8_t*>;
+
+ std::shared_ptr<ProducerChannel> GetProducer() const;
+
+ std::pair<BorrowedFence, MetaData> OnConsumerAcquire(
+ Message& message, std::size_t metadata_size);
+ int OnConsumerRelease(Message& message, LocalFence release_fence);
+ int OnConsumerSetIgnore(Message& message, bool ignore);
+
+ bool handled_; // True if we have processed RELEASE.
+ bool ignored_; // True if we are ignoring events.
+ std::weak_ptr<Channel> producer_;
+
+ ConsumerChannel(const ConsumerChannel&) = delete;
+ void operator=(const ConsumerChannel&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFERHUBD_CONSUMER_CHANNEL_H_
diff --git a/services/vr/bufferhubd/consumer_queue_channel.cpp b/services/vr/bufferhubd/consumer_queue_channel.cpp
new file mode 100644
index 0000000..39d6bc8
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_queue_channel.cpp
@@ -0,0 +1,122 @@
+#include "consumer_queue_channel.h"
+
+#include <pdx/channel_handle.h>
+
+#include "producer_channel.h"
+
+using android::pdx::RemoteChannelHandle;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+ConsumerQueueChannel::ConsumerQueueChannel(
+ BufferHubService* service, int buffer_id, int channel_id,
+ const std::shared_ptr<Channel>& producer)
+ : BufferHubChannel(service, buffer_id, channel_id, kConsumerQueueType),
+ producer_(producer),
+ capacity_(0) {
+ GetProducer()->AddConsumer(this);
+}
+
+ConsumerQueueChannel::~ConsumerQueueChannel() {
+ ALOGD_IF(TRACE, "ConsumerQueueChannel::~ConsumerQueueChannel: channel_id=%d",
+ channel_id());
+
+ if (auto producer = GetProducer()) {
+ producer->RemoveConsumer(this);
+ }
+}
+
+bool ConsumerQueueChannel::HandleMessage(Message& message) {
+ ATRACE_NAME("ConsumerQueueChannel::HandleMessage");
+ auto producer = GetProducer();
+ if (!producer)
+ REPLY_ERROR_RETURN(message, EPIPE, true);
+
+ switch (message.GetOp()) {
+ case BufferHubRPC::CreateConsumerQueue::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::CreateConsumerQueue>(
+ *producer, &ProducerQueueChannel::OnCreateConsumerQueue, message);
+ return true;
+
+ case BufferHubRPC::ConsumerQueueImportBuffers::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>(
+ *this, &ConsumerQueueChannel::OnConsumerQueueImportBuffers, message);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+std::shared_ptr<ProducerQueueChannel> ConsumerQueueChannel::GetProducer()
+ const {
+ return std::static_pointer_cast<ProducerQueueChannel>(producer_.lock());
+}
+
+void ConsumerQueueChannel::HandleImpulse(Message& /* message */) {
+ ATRACE_NAME("ConsumerQueueChannel::HandleImpulse");
+}
+
+BufferHubChannel::BufferInfo ConsumerQueueChannel::GetBufferInfo() const {
+ BufferHubChannel::BufferInfo info;
+ if (auto producer = GetProducer()) {
+ // If producer has not hung up, copy most buffer info from the producer.
+ info = producer->GetBufferInfo();
+ }
+ info.id = buffer_id();
+ info.capacity = capacity_;
+ return info;
+}
+
+void ConsumerQueueChannel::RegisterNewBuffer(
+ const std::shared_ptr<ProducerChannel>& producer_channel, size_t slot) {
+ pending_buffer_slots_.emplace(producer_channel, slot);
+
+ // Signal the client that there is new buffer available throught POLLIN.
+ SignalAvailable();
+}
+
+std::vector<std::pair<RemoteChannelHandle, size_t>>
+ConsumerQueueChannel::OnConsumerQueueImportBuffers(Message& message) {
+ std::vector<std::pair<RemoteChannelHandle, size_t>> buffer_handles;
+ ATRACE_NAME("ConsumerQueueChannel::OnConsumerQueueImportBuffers");
+ ALOGD(
+ "ConsumerQueueChannel::OnConsumerQueueImportBuffers number of buffers to "
+ "import: %zu",
+ pending_buffer_slots_.size());
+
+ while (!pending_buffer_slots_.empty()) {
+ auto producer_channel = pending_buffer_slots_.front().first.lock();
+ size_t producer_slot = pending_buffer_slots_.front().second;
+ pending_buffer_slots_.pop();
+
+ // It's possible that the producer channel has expired.
+ if (producer_channel == nullptr) {
+ ALOGE(
+ "ConsumerQueueChannel::OnConsumerQueueImportBuffers: producer "
+ "channel has already been expired.");
+ REPLY_ERROR_RETURN(message, ENOENT, {});
+ }
+
+ RemoteChannelHandle consumer_handle(
+ producer_channel->CreateConsumer(message));
+
+ // All buffer imports should succeed together.
+ if (!consumer_handle.valid()) {
+ ALOGE(
+ "ConsumerQueueChannel::OnConsumerQueueImportBuffers: imported "
+ "consumer handle is invalid.");
+ REPLY_ERROR_RETURN(message, EIO, {});
+ }
+
+ // Move consumer_handle into buffer_handles.
+ buffer_handles.emplace_back(std::move(consumer_handle), producer_slot);
+ }
+
+ return buffer_handles;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/bufferhubd/consumer_queue_channel.h b/services/vr/bufferhubd/consumer_queue_channel.h
new file mode 100644
index 0000000..b345595
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_queue_channel.h
@@ -0,0 +1,62 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_CONSUMER_QUEUE_CHANNEL_H_
+#define ANDROID_DVR_BUFFERHUBD_CONSUMER_QUEUE_CHANNEL_H_
+
+#include "buffer_hub.h"
+
+#include <private/dvr/bufferhub_rpc.h>
+
+#include <queue>
+
+#include "consumer_channel.h"
+#include "producer_queue_channel.h"
+
+namespace android {
+namespace dvr {
+
+class ConsumerQueueChannel : public BufferHubChannel {
+ public:
+ using Message = pdx::Message;
+ using RemoteChannelHandle = pdx::RemoteChannelHandle;
+
+ ConsumerQueueChannel(BufferHubService* service, int buffer_id, int channel_id,
+ const std::shared_ptr<Channel>& producer);
+ ~ConsumerQueueChannel() override;
+
+ bool HandleMessage(Message& message) override;
+ void HandleImpulse(Message& message) override;
+
+ BufferInfo GetBufferInfo() const override;
+
+ // Called by ProdcuerQueueChannel to notify consumer queue that a new
+ // buffer has been allocated.
+ void RegisterNewBuffer(
+ const std::shared_ptr<ProducerChannel>& producer_channel, size_t slot);
+
+ // Called after clients been signaled by service that new buffer has been
+ // allocated. Clients uses kOpConsumerQueueImportBuffers to import new
+ // consumer buffers and this handler returns a vector of fd representing
+ // BufferConsumers that clients can import.
+ std::vector<std::pair<RemoteChannelHandle, size_t>>
+ OnConsumerQueueImportBuffers(Message& message);
+
+ private:
+ std::shared_ptr<ProducerQueueChannel> GetProducer() const;
+
+ // Pointer to the prodcuer channel
+ std::weak_ptr<Channel> producer_;
+
+ // Tracks newly allocated buffer producers along with it's slot number.
+ std::queue<std::pair<std::weak_ptr<ProducerChannel>, size_t>>
+ pending_buffer_slots_;
+
+ // Tracks how many buffers have this queue imported.
+ size_t capacity_;
+
+ ConsumerQueueChannel(const ConsumerQueueChannel&) = delete;
+ void operator=(const ConsumerQueueChannel&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFERHUBD_CONSUMER_QUEUE_CHANNEL_H_
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
new file mode 100644
index 0000000..b87b709
--- /dev/null
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -0,0 +1,377 @@
+#include "producer_channel.h"
+
+#include <cutils/log.h>
+#include <sync/sync.h>
+#include <sys/poll.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <atomic>
+#include <thread>
+
+#include <base/logging.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include "consumer_channel.h"
+
+using android::pdx::BorrowedHandle;
+using android::pdx::Message;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::rpc::BufferWrapper;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::WrapBuffer;
+
+namespace android {
+namespace dvr {
+
+ProducerChannel::ProducerChannel(BufferHubService* service, int channel_id,
+ int width, int height, int format, int usage,
+ size_t meta_size_bytes, size_t slice_count,
+ int* error)
+ : BufferHubChannel(service, channel_id, channel_id, kProducerType),
+ pending_consumers_(0),
+ slices_(std::max(static_cast<size_t>(1), slice_count)),
+ producer_owns_(true),
+ meta_size_bytes_(meta_size_bytes),
+ meta_(meta_size_bytes ? new uint8_t[meta_size_bytes] : nullptr) {
+ for (auto& ion_buffer : slices_) {
+ const int ret = ion_buffer.Alloc(width, height, format, usage);
+ if (ret < 0) {
+ ALOGE("ProducerChannel::ProducerChannel: Failed to allocate buffer: %s",
+ strerror(-ret));
+ *error = ret;
+ return;
+ }
+ }
+
+ // Success.
+ *error = 0;
+}
+
+std::shared_ptr<ProducerChannel> ProducerChannel::Create(
+ BufferHubService* service, int channel_id, int width, int height,
+ int format, int usage, size_t meta_size_bytes, size_t slice_count,
+ int* error) {
+ std::shared_ptr<ProducerChannel> producer(
+ new ProducerChannel(service, channel_id, width, height, format, usage,
+ meta_size_bytes, slice_count, error));
+ if (*error < 0)
+ return nullptr;
+ else
+ return producer;
+}
+
+ProducerChannel::~ProducerChannel() {
+ ALOGD_IF(TRACE, "ProducerChannel::~ProducerChannel: channel_id=%d",
+ channel_id());
+ for (auto consumer : consumer_channels_)
+ consumer->OnProducerClosed();
+}
+
+BufferHubChannel::BufferInfo ProducerChannel::GetBufferInfo() const {
+ return BufferInfo(buffer_id(), consumer_channels_.size(), slices_[0].width(),
+ slices_[0].height(), slices_[0].format(),
+ slices_[0].usage(), slices_.size(), name_);
+}
+
+void ProducerChannel::HandleImpulse(Message& message) {
+ ATRACE_NAME("ProducerChannel::HandleImpulse");
+ switch (message.GetOp()) {
+ case BufferHubRPC::ProducerGain::Opcode:
+ OnProducerGain(message);
+ break;
+ }
+}
+
+bool ProducerChannel::HandleMessage(Message& message) {
+ ATRACE_NAME("ProducerChannel::HandleMessage");
+ switch (message.GetOp()) {
+ case BufferHubRPC::GetBuffer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::GetBuffer>(
+ *this, &ProducerChannel::OnGetBuffer, message);
+ return true;
+
+ case BufferHubRPC::GetBuffers::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::GetBuffers>(
+ *this, &ProducerChannel::OnGetBuffers, message);
+ return true;
+
+ case BufferHubRPC::NewConsumer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::NewConsumer>(
+ *this, &ProducerChannel::OnNewConsumer, message);
+ return true;
+
+ case BufferHubRPC::ProducerPost::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ProducerPost>(
+ *this, &ProducerChannel::OnProducerPost, message);
+ return true;
+
+ case BufferHubRPC::ProducerGain::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ProducerGain>(
+ *this, &ProducerChannel::OnProducerGain, message);
+ return true;
+
+ case BufferHubRPC::ProducerMakePersistent::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ProducerMakePersistent>(
+ *this, &ProducerChannel::OnProducerMakePersistent, message);
+ return true;
+
+ case BufferHubRPC::ProducerRemovePersistence::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ProducerRemovePersistence>(
+ *this, &ProducerChannel::OnRemovePersistence, message);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+NativeBufferHandle<BorrowedHandle> ProducerChannel::OnGetBuffer(
+ Message& message, unsigned index) {
+ ATRACE_NAME("ProducerChannel::OnGetBuffer");
+ ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffer: buffer=%d", buffer_id());
+ if (index < slices_.size()) {
+ return NativeBufferHandle<BorrowedHandle>(slices_[index], buffer_id());
+ } else {
+ REPLY_ERROR_RETURN(message, EINVAL, NativeBufferHandle<BorrowedHandle>());
+ }
+}
+
+std::vector<NativeBufferHandle<BorrowedHandle>> ProducerChannel::OnGetBuffers(
+ Message&) {
+ ATRACE_NAME("ProducerChannel::OnGetBuffers");
+ ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffers: buffer_id=%d", buffer_id());
+ std::vector<NativeBufferHandle<BorrowedHandle>> buffer_handles;
+ for (const auto& buffer : slices_)
+ buffer_handles.emplace_back(buffer, buffer_id());
+ return buffer_handles;
+}
+
+RemoteChannelHandle ProducerChannel::CreateConsumer(Message& message) {
+ ATRACE_NAME("ProducerChannel::CreateConsumer");
+ ALOGD_IF(TRACE, "ProducerChannel::CreateConsumer: buffer_id=%d", buffer_id());
+
+ int channel_id;
+ auto status = message.PushChannel(0, nullptr, &channel_id);
+ if (!status) {
+ ALOGE(
+ "ProducerChannel::CreateConsumer: failed to push consumer channel: %s",
+ status.GetErrorMessage().c_str());
+ return RemoteChannelHandle();
+ }
+
+ auto consumer = std::make_shared<ConsumerChannel>(
+ service(), buffer_id(), channel_id, shared_from_this());
+ const int ret = service()->SetChannel(channel_id, consumer);
+ if (ret < 0) {
+ ALOGE(
+ "ProducerChannel::CreateConsumer: failed to set new consumer channel: "
+ "%s",
+ strerror(-ret));
+ return RemoteChannelHandle();
+ }
+
+ if (!producer_owns_) {
+ // Signal the new consumer when adding it to a posted producer.
+ if (consumer->OnProducerPosted())
+ pending_consumers_++;
+ }
+
+ return status.take();
+}
+
+RemoteChannelHandle ProducerChannel::OnNewConsumer(Message& message) {
+ ATRACE_NAME("ProducerChannel::OnNewConsumer");
+ ALOGD_IF(TRACE, "ProducerChannel::OnNewConsumer: buffer_id=%d", buffer_id());
+
+ RemoteChannelHandle consumer_handle(CreateConsumer(message));
+
+ if (consumer_handle.valid())
+ return consumer_handle;
+ else
+ REPLY_ERROR_RETURN(message, ENOMEM, RemoteChannelHandle());
+}
+
+int ProducerChannel::OnProducerPost(
+ Message&, LocalFence acquire_fence,
+ BufferWrapper<std::vector<std::uint8_t>> metadata) {
+ ATRACE_NAME("ProducerChannel::OnProducerPost");
+ ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: buffer_id=%d", buffer_id());
+ if (!producer_owns_) {
+ ALOGE("ProducerChannel::OnProducerPost: Not in gained state!");
+ return -EBUSY;
+ }
+
+ if (meta_size_bytes_ != metadata.size())
+ return -EINVAL;
+ std::copy(metadata.begin(), metadata.end(), meta_.get());
+
+ post_fence_ = std::move(acquire_fence);
+ producer_owns_ = false;
+
+ // Signal any interested consumers. If there are none, automatically release
+ // the buffer.
+ pending_consumers_ = 0;
+ for (auto consumer : consumer_channels_) {
+ if (consumer->OnProducerPosted())
+ pending_consumers_++;
+ }
+ if (pending_consumers_ == 0)
+ SignalAvailable();
+ ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: %d pending consumers",
+ pending_consumers_);
+
+ return 0;
+}
+
+LocalFence ProducerChannel::OnProducerGain(Message& message) {
+ ATRACE_NAME("ProducerChannel::OnGain");
+ ALOGD_IF(TRACE, "ProducerChannel::OnGain: buffer_id=%d", buffer_id());
+ if (producer_owns_) {
+ ALOGE("ProducerChanneL::OnGain: Already in gained state: channel=%d",
+ channel_id());
+ REPLY_ERROR_RETURN(message, EALREADY, {});
+ }
+
+ // There are still pending consumers, return busy.
+ if (pending_consumers_ > 0)
+ REPLY_ERROR_RETURN(message, EBUSY, {});
+
+ ClearAvailable();
+ producer_owns_ = true;
+ post_fence_.get_fd();
+ return std::move(returned_fence_);
+}
+
+std::pair<BorrowedFence, BufferWrapper<std::uint8_t*>>
+ProducerChannel::OnConsumerAcquire(Message& message,
+ std::size_t metadata_size) {
+ ATRACE_NAME("ProducerChannel::OnConsumerAcquire");
+ ALOGD_IF(TRACE, "ProducerChannel::OnConsumerAcquire: buffer_id=%d",
+ buffer_id());
+ if (producer_owns_) {
+ ALOGE("ProducerChannel::OnConsumerAcquire: Not in posted state!");
+ REPLY_ERROR_RETURN(message, EBUSY, {});
+ }
+
+ // Return a borrowed fd to avoid unnecessary duplication of the underlying fd.
+ // Serialization just needs to read the handle.
+ if (metadata_size == 0)
+ return std::make_pair(post_fence_.borrow(),
+ WrapBuffer<std::uint8_t>(nullptr, 0));
+ else
+ return std::make_pair(post_fence_.borrow(),
+ WrapBuffer(meta_.get(), meta_size_bytes_));
+}
+
+int ProducerChannel::OnConsumerRelease(Message&, LocalFence release_fence) {
+ ATRACE_NAME("ProducerChannel::OnConsumerRelease");
+ ALOGD_IF(TRACE, "ProducerChannel::OnConsumerRelease: buffer_id=%d",
+ buffer_id());
+ if (producer_owns_) {
+ ALOGE("ProducerChannel::OnConsumerRelease: Not in acquired state!");
+ return -EBUSY;
+ }
+
+ // Attempt to merge the fences if necessary.
+ if (release_fence) {
+ if (returned_fence_) {
+ LocalFence merged_fence(sync_merge(
+ "bufferhub_merged", returned_fence_.get_fd(), release_fence.get_fd()));
+ const int error = errno;
+ if (!merged_fence) {
+ ALOGE("ProducerChannel::OnConsumerRelease: Failed to merge fences: %s",
+ strerror(error));
+ return -error;
+ }
+ returned_fence_ = std::move(merged_fence);
+ } else {
+ returned_fence_ = std::move(release_fence);
+ }
+ }
+
+ OnConsumerIgnored();
+ return 0;
+}
+
+void ProducerChannel::OnConsumerIgnored() {
+ if (!--pending_consumers_)
+ SignalAvailable();
+ ALOGD_IF(TRACE,
+ "ProducerChannel::OnConsumerIgnored: buffer_id=%d %d consumers left",
+ buffer_id(), pending_consumers_);
+}
+
+int ProducerChannel::OnProducerMakePersistent(Message& message,
+ const std::string& name,
+ int user_id, int group_id) {
+ ATRACE_NAME("ProducerChannel::OnProducerMakePersistent");
+ ALOGD_IF(TRACE,
+ "ProducerChannel::OnProducerMakePersistent: buffer_id=%d name=%s "
+ "user_id=%d group_id=%d",
+ buffer_id(), name.c_str(), user_id, group_id);
+
+ if (name.empty() || (user_id < 0 && user_id != kNoCheckId) ||
+ (group_id < 0 && group_id != kNoCheckId)) {
+ return -EINVAL;
+ }
+
+ // Try to add this buffer with the requested name.
+ if (service()->AddNamedBuffer(name, std::static_pointer_cast<ProducerChannel>(
+ shared_from_this()))) {
+ // If successful, set the requested permissions.
+
+ // A value of zero indicates that the ids from the sending process should be
+ // used.
+ if (user_id == kUseCallerId)
+ user_id = message.GetEffectiveUserId();
+ if (group_id == kUseCallerId)
+ group_id = message.GetEffectiveGroupId();
+
+ owner_user_id_ = user_id;
+ owner_group_id_ = group_id;
+ name_ = name;
+ return 0;
+ } else {
+ // Otherwise a buffer with that name already exists.
+ return -EALREADY;
+ }
+}
+
+int ProducerChannel::OnRemovePersistence(Message&) {
+ if (service()->RemoveNamedBuffer(*this))
+ return 0;
+ else
+ return -ENOENT;
+}
+
+void ProducerChannel::AddConsumer(ConsumerChannel* channel) {
+ consumer_channels_.push_back(channel);
+}
+
+void ProducerChannel::RemoveConsumer(ConsumerChannel* channel) {
+ consumer_channels_.erase(
+ std::find(consumer_channels_.begin(), consumer_channels_.end(), channel));
+}
+
+// Returns true if either the user or group ids match the owning ids or both
+// owning ids are not set, in which case access control does not apply.
+bool ProducerChannel::CheckAccess(int euid, int egid) {
+ const bool no_check =
+ owner_user_id_ == kNoCheckId && owner_group_id_ == kNoCheckId;
+ const bool euid_check = euid == owner_user_id_ || euid == kRootId;
+ const bool egid_check = egid == owner_group_id_ || egid == kRootId;
+ return no_check || euid_check || egid_check;
+}
+
+// Returns true if the given parameters match the underlying buffer parameters.
+bool ProducerChannel::CheckParameters(int width, int height, int format,
+ int usage, size_t meta_size_bytes,
+ size_t slice_count) {
+ return slices_.size() == slice_count &&
+ meta_size_bytes == meta_size_bytes_ && slices_[0].width() == width &&
+ slices_[0].height() == height && slices_[0].format() == format &&
+ slices_[0].usage() == usage;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/bufferhubd/producer_channel.h b/services/vr/bufferhubd/producer_channel.h
new file mode 100644
index 0000000..e7ca459
--- /dev/null
+++ b/services/vr/bufferhubd/producer_channel.h
@@ -0,0 +1,109 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_
+#define ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_
+
+#include "buffer_hub.h"
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/buffer_wrapper.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+// The buffer changes ownership according to the following sequence:
+// POST -> ACQUIRE/RELEASE (all consumers) -> GAIN (producer acquires) -> POST
+
+// The producer channel is owned by a single app that writes into buffers and
+// calls POST when drawing is complete. This channel has a set of consumer
+// channels associated with it that are waiting for notifications.
+class ProducerChannel : public BufferHubChannel {
+ public:
+ using Message = pdx::Message;
+ using BorrowedHandle = pdx::BorrowedHandle;
+ using RemoteChannelHandle = pdx::RemoteChannelHandle;
+ template <typename T>
+ using BufferWrapper = pdx::rpc::BufferWrapper<T>;
+
+ static std::shared_ptr<ProducerChannel> Create(
+ BufferHubService* service, int channel_id, int width, int height,
+ int format, int usage, size_t meta_size_bytes, size_t slice_count,
+ int* error);
+
+ ~ProducerChannel() override;
+
+ bool HandleMessage(Message& message) override;
+ void HandleImpulse(Message& message) override;
+
+ BufferInfo GetBufferInfo() const override;
+
+ NativeBufferHandle<BorrowedHandle> OnGetBuffer(Message& message,
+ unsigned index);
+ std::vector<NativeBufferHandle<BorrowedHandle>> OnGetBuffers(
+ Message& message);
+
+ RemoteChannelHandle CreateConsumer(Message& message);
+ RemoteChannelHandle OnNewConsumer(Message& message);
+
+ std::pair<BorrowedFence, BufferWrapper<std::uint8_t*>> OnConsumerAcquire(
+ Message& message, std::size_t metadata_size);
+ int OnConsumerRelease(Message& message, LocalFence release_fence);
+
+ void OnConsumerIgnored();
+
+ void AddConsumer(ConsumerChannel* channel);
+ void RemoveConsumer(ConsumerChannel* channel);
+
+ bool CheckAccess(int euid, int egid);
+ bool CheckParameters(int width, int height, int format, int usage,
+ size_t meta_size_bytes, size_t slice_count);
+
+ int OnProducerMakePersistent(Message& message, const std::string& name,
+ int user_id, int group_id);
+ int OnRemovePersistence(Message& message);
+
+ private:
+ std::vector<ConsumerChannel*> consumer_channels_;
+ // This counts the number of consumers left to process this buffer. If this is
+ // zero then the producer can re-acquire ownership.
+ int pending_consumers_;
+
+ std::vector<IonBuffer> slices_;
+
+ bool producer_owns_;
+ LocalFence post_fence_;
+ LocalFence returned_fence_;
+ size_t meta_size_bytes_;
+ std::unique_ptr<uint8_t[]> meta_;
+
+ static constexpr int kNoCheckId = -1;
+ static constexpr int kUseCallerId = 0;
+ static constexpr int kRootId = 0;
+
+ // User and group id to check when obtaining a persistent buffer.
+ int owner_user_id_ = kNoCheckId;
+ int owner_group_id_ = kNoCheckId;
+
+ std::string name_;
+
+ ProducerChannel(BufferHubService* service, int channel, int width, int height,
+ int format, int usage, size_t meta_size_bytes,
+ size_t slice_count, int* error);
+
+ int OnProducerPost(Message& message, LocalFence acquire_fence,
+ BufferWrapper<std::vector<std::uint8_t>> metadata);
+ LocalFence OnProducerGain(Message& message);
+
+ ProducerChannel(const ProducerChannel&) = delete;
+ void operator=(const ProducerChannel&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_
diff --git a/services/vr/bufferhubd/producer_queue_channel.cpp b/services/vr/bufferhubd/producer_queue_channel.cpp
new file mode 100644
index 0000000..08f1e9d
--- /dev/null
+++ b/services/vr/bufferhubd/producer_queue_channel.cpp
@@ -0,0 +1,287 @@
+#include "producer_queue_channel.h"
+
+#include "consumer_queue_channel.h"
+#include "producer_channel.h"
+
+using android::pdx::RemoteChannelHandle;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+ProducerQueueChannel::ProducerQueueChannel(
+ BufferHubService* service, int channel_id, size_t meta_size_bytes,
+ int usage_set_mask, int usage_clear_mask, int usage_deny_set_mask,
+ int usage_deny_clear_mask, int* error)
+ : BufferHubChannel(service, channel_id, channel_id, kProducerQueueType),
+ meta_size_bytes_(meta_size_bytes),
+ usage_set_mask_(usage_set_mask),
+ usage_clear_mask_(usage_clear_mask),
+ usage_deny_set_mask_(usage_deny_set_mask),
+ usage_deny_clear_mask_(usage_deny_clear_mask),
+ capacity_(0) {
+ *error = 0;
+}
+
+ProducerQueueChannel::~ProducerQueueChannel() {}
+
+/* static */
+std::shared_ptr<ProducerQueueChannel> ProducerQueueChannel::Create(
+ BufferHubService* service, int channel_id, size_t meta_size_bytes,
+ int usage_set_mask, int usage_clear_mask, int usage_deny_set_mask,
+ int usage_deny_clear_mask, int* error) {
+ // Configuration between |usage_deny_set_mask| and |usage_deny_clear_mask|
+ // should be mutually exclusive.
+ if (usage_deny_set_mask & usage_deny_clear_mask) {
+ ALOGE(
+ "BufferHubService::OnCreateProducerQueue: illegal usage mask "
+ "configuration: usage_deny_set_mask=%d, usage_deny_clear_mask=%d",
+ usage_deny_set_mask, usage_deny_clear_mask);
+ *error = -EINVAL;
+ return nullptr;
+ }
+
+ std::shared_ptr<ProducerQueueChannel> producer(new ProducerQueueChannel(
+ service, channel_id, meta_size_bytes, usage_set_mask, usage_clear_mask,
+ usage_deny_set_mask, usage_deny_clear_mask, error));
+ if (*error < 0)
+ return nullptr;
+ else
+ return producer;
+}
+
+bool ProducerQueueChannel::HandleMessage(Message& message) {
+ ATRACE_NAME("ProducerQueueChannel::HandleMessage");
+ switch (message.GetOp()) {
+ case BufferHubRPC::CreateConsumerQueue::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::CreateConsumerQueue>(
+ *this, &ProducerQueueChannel::OnCreateConsumerQueue, message);
+ return true;
+
+ case BufferHubRPC::ProducerQueueAllocateBuffers::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>(
+ *this, &ProducerQueueChannel::OnProducerQueueAllocateBuffers,
+ message);
+ return true;
+
+ case BufferHubRPC::ProducerQueueDetachBuffer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ProducerQueueDetachBuffer>(
+ *this, &ProducerQueueChannel::OnProducerQueueDetachBuffer, message);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+void ProducerQueueChannel::HandleImpulse(Message& /* message */) {
+ ATRACE_NAME("ProducerQueueChannel::HandleImpulse");
+}
+
+BufferHubChannel::BufferInfo ProducerQueueChannel::GetBufferInfo() const {
+ return BufferInfo(channel_id(), consumer_channels_.size(), capacity_,
+ usage_set_mask_, usage_clear_mask_, usage_deny_set_mask_,
+ usage_deny_clear_mask_);
+}
+
+std::pair<RemoteChannelHandle, size_t>
+ProducerQueueChannel::OnCreateConsumerQueue(Message& message) {
+ ATRACE_NAME("ProducerQueueChannel::OnCreateConsumerQueue");
+ ALOGD_IF(TRACE, "ProducerQueueChannel::OnCreateConsumerQueue: channel_id=%d",
+ channel_id());
+
+ int channel_id;
+ auto status = message.PushChannel(0, nullptr, &channel_id);
+ if (!status) {
+ ALOGE(
+ "ProducerQueueChannel::OnCreateConsumerQueue: failed to push consumer "
+ "channel: %s",
+ status.GetErrorMessage().c_str());
+ REPLY_ERROR_RETURN(message, ENOMEM, {});
+ }
+
+ const int ret = service()->SetChannel(
+ channel_id, std::make_shared<ConsumerQueueChannel>(
+ service(), buffer_id(), channel_id, shared_from_this()));
+ if (ret < 0) {
+ ALOGE(
+ "ProducerQueueChannel::OnCreateConsumerQueue: failed to set new "
+ "consumer channel: %s",
+ strerror(-ret));
+ REPLY_ERROR_RETURN(message, ENOMEM, {});
+ }
+
+ return std::make_pair(status.take(), meta_size_bytes_);
+}
+
+std::vector<std::pair<RemoteChannelHandle, size_t>>
+ProducerQueueChannel::OnProducerQueueAllocateBuffers(Message& message,
+ int width, int height,
+ int format, int usage,
+ size_t slice_count,
+ size_t buffer_count) {
+ ATRACE_NAME("ProducerQueueChannel::OnProducerQueueAllocateBuffers");
+ ALOGD_IF(TRACE,
+ "ProducerQueueChannel::OnProducerQueueAllocateBuffers: "
+ "producer_channel_id=%d",
+ channel_id());
+
+ std::vector<std::pair<RemoteChannelHandle, size_t>> buffer_handles;
+
+ // Deny buffer allocation violating preset rules.
+ if (usage & usage_deny_set_mask_) {
+ ALOGE(
+ "ProducerQueueChannel::OnProducerQueueAllocateBuffers: usage: %d is "
+ "not permitted. Violating usage_deny_set_mask, the following bits "
+ "shall not be set: %d.",
+ usage, usage_deny_set_mask_);
+ REPLY_ERROR_RETURN(message, EINVAL, buffer_handles);
+ }
+
+ if (~usage & usage_deny_clear_mask_) {
+ ALOGE(
+ "ProducerQueueChannel::OnProducerQueueAllocateBuffers: usage: %d is "
+ "not permitted. Violating usage_deny_clear_mask, the following bits "
+ "must be set: %d.",
+ usage, usage_deny_clear_mask_);
+ REPLY_ERROR_RETURN(message, EINVAL, buffer_handles);
+ }
+
+ // Force set mask and clear mask. Note that |usage_set_mask_| takes precedence
+ // and will overwrite |usage_clear_mask_|.
+ int effective_usage = (usage & ~usage_clear_mask_) | usage_set_mask_;
+
+ for (size_t i = 0; i < buffer_count; i++) {
+ auto buffer_handle_slot = AllocateBuffer(message, width, height, format,
+ effective_usage, slice_count);
+ if (!buffer_handle_slot.first) {
+ ALOGE(
+ "ProducerQueueChannel::OnProducerQueueAllocateBuffers: failed to "
+ "allocate new buffer.");
+ REPLY_ERROR_RETURN(message, ENOMEM, buffer_handles);
+ }
+ buffer_handles.emplace_back(std::move(buffer_handle_slot.first),
+ buffer_handle_slot.second);
+ }
+
+ return buffer_handles;
+}
+
+std::pair<RemoteChannelHandle, size_t> ProducerQueueChannel::AllocateBuffer(
+ Message& message, int width, int height, int format, int usage,
+ size_t slice_count) {
+ ATRACE_NAME("ProducerQueueChannel::AllocateBuffer");
+ ALOGD_IF(TRACE,
+ "ProducerQueueChannel::AllocateBuffer: producer_channel_id=%d",
+ channel_id());
+
+ if (capacity_ >= BufferHubRPC::kMaxQueueCapacity) {
+ ALOGE("ProducerQueueChannel::AllocateBuffer: reaches kMaxQueueCapacity.");
+ return {};
+ }
+
+ // Here we are creating a new BufferHubBuffer, initialize the producer
+ // channel, and returning its file handle back to the client.
+ // buffer_id is the id of the producer channel of BufferHubBuffer.
+ int buffer_id;
+ auto status = message.PushChannel(0, nullptr, &buffer_id);
+
+ if (!status) {
+ ALOGE("ProducerQueueChannel::AllocateBuffer: failed to push channel: %s",
+ status.GetErrorMessage().c_str());
+ return {};
+ }
+
+ ALOGD_IF(TRACE,
+ "ProducerQueueChannel::AllocateBuffer: buffer_id=%d width=%d "
+ "height=%d format=%d usage=%d slice_count=%zu",
+ buffer_id, width, height, format, usage, slice_count);
+ auto buffer_handle = status.take();
+
+ int error;
+ const auto producer_channel = ProducerChannel::Create(
+ service(), buffer_id, width, height, format, usage,
+ meta_size_bytes_, slice_count, &error);
+ if (!producer_channel) {
+ ALOGE(
+ "ProducerQueueChannel::AllocateBuffer: Failed to create "
+ "BufferHubBuffer producer!!");
+ return {};
+ }
+
+ ALOGD_IF(
+ TRACE,
+ "ProducerQueueChannel::AllocateBuffer: buffer_id=%d, buffer_handle=%d",
+ buffer_id, buffer_handle.value());
+
+ const int ret = service()->SetChannel(buffer_id, producer_channel);
+ if (ret < 0) {
+ ALOGE(
+ "ProducerQueueChannel::AllocateBuffer: failed to set prodcuer channel "
+ "for new BufferHubBuffer: %s",
+ strerror(-ret));
+ return {};
+ }
+
+ // Register the newly allocated 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 {};
+ }
+
+ 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_id=%d",
+ buffer_id);
+ consumer_channel->RegisterNewBuffer(producer_channel, slot);
+ }
+
+ return {std::move(buffer_handle), slot};
+}
+
+int ProducerQueueChannel::OnProducerQueueDetachBuffer(Message& message,
+ size_t slot) {
+ if (buffers_[slot].expired()) {
+ ALOGE(
+ "ProducerQueueChannel::OnProducerQueueDetachBuffer: trying to detach "
+ "an invalid buffer producer at slot %zu",
+ slot);
+ return -EINVAL;
+ }
+
+ if (capacity_ == 0) {
+ ALOGE(
+ "ProducerQueueChannel::OnProducerQueueDetachBuffer: trying to detach a "
+ "buffer producer while the queue's capacity is already zero.");
+ return -EINVAL;
+ }
+
+ buffers_[slot].reset();
+ capacity_--;
+ return 0;
+}
+
+void ProducerQueueChannel::AddConsumer(ConsumerQueueChannel* channel) {
+ consumer_channels_.push_back(channel);
+}
+
+void ProducerQueueChannel::RemoveConsumer(ConsumerQueueChannel* channel) {
+ consumer_channels_.erase(
+ std::find(consumer_channels_.begin(), consumer_channels_.end(), channel));
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/bufferhubd/producer_queue_channel.h b/services/vr/bufferhubd/producer_queue_channel.h
new file mode 100644
index 0000000..49611d4
--- /dev/null
+++ b/services/vr/bufferhubd/producer_queue_channel.h
@@ -0,0 +1,96 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_
+#define ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_
+
+#include "buffer_hub.h"
+
+#include <private/dvr/bufferhub_rpc.h>
+
+namespace android {
+namespace dvr {
+
+class ProducerQueueChannel : public BufferHubChannel {
+ public:
+ using Message = pdx::Message;
+ using RemoteChannelHandle = pdx::RemoteChannelHandle;
+
+ static std::shared_ptr<ProducerQueueChannel> Create(
+ BufferHubService* service, int channel_id, size_t meta_size_bytes,
+ int usage_set_mask, int usage_clear_mask, int usage_deny_set_mask,
+ int usage_deny_clear_mask, int* error);
+ ~ProducerQueueChannel() override;
+
+ bool HandleMessage(Message& message) override;
+ void HandleImpulse(Message& message) override;
+
+ BufferInfo GetBufferInfo() const override;
+
+ // Handles client request to create a new consumer queue attached to current
+ // producer queue.
+ // Returns a handle for the service channel, as well as the size of the
+ // metadata associated with the queue.
+ std::pair<RemoteChannelHandle, size_t> OnCreateConsumerQueue(
+ Message& message);
+
+ // Allocate a new BufferHubProducer according to the input spec. Client may
+ // handle this as if a new producer is created through kOpCreateBuffer.
+ std::vector<std::pair<RemoteChannelHandle, size_t>>
+ OnProducerQueueAllocateBuffers(Message& message, int width, int height,
+ int format, int usage, size_t slice_count,
+ 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.
+ int OnProducerQueueDetachBuffer(Message& message, size_t slot);
+
+ void AddConsumer(ConsumerQueueChannel* channel);
+ void RemoveConsumer(ConsumerQueueChannel* channel);
+
+ private:
+ ProducerQueueChannel(BufferHubService* service, int channel_id,
+ size_t meta_size_bytes, int usage_set_mask,
+ int usage_clear_mask, int usage_deny_set_mask,
+ int usage_deny_clear_mask, int* error);
+
+ // Allocate one single producer buffer by |OnProducerQueueAllocateBuffers|.
+ // Note that the newly created buffer's file handle will be pushed to client
+ // and our return type is a RemoteChannelHandle.
+ // Returns the remote channdel handle and the slot number for the newly
+ // allocated buffer.
+ std::pair<RemoteChannelHandle, size_t> AllocateBuffer(Message& message,
+ int width, int height,
+ int format, int usage,
+ size_t slice_count);
+
+ // Size of the meta data associated with all the buffers allocated from the
+ // queue. Now we assume the metadata size is immutable once the queue is
+ // created.
+ size_t meta_size_bytes_;
+
+ // A set of variables to control what |usage| bits can this ProducerQueue
+ // allocate.
+ int usage_set_mask_;
+ int usage_clear_mask_;
+ int usage_deny_set_mask_;
+ int usage_deny_clear_mask_;
+
+ // Provides access to the |channel_id| of all consumer channels associated
+ // with this producer.
+ std::vector<ConsumerQueueChannel*> consumer_channels_;
+
+ // Tracks how many buffers have this queue allocated.
+ size_t capacity_;
+
+ // Tracks of all buffer producer allocated through this buffer queue. Once
+ // a buffer get allocated, it will take a logical slot in the |buffers_| array
+ // and the slot number will stay unchanged during the entire life cycle of the
+ // queue.
+ std::weak_ptr<ProducerChannel> buffers_[BufferHubRPC::kMaxQueueCapacity];
+
+ ProducerQueueChannel(const ProducerQueueChannel&) = delete;
+ void operator=(const ProducerQueueChannel&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_