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/libs/vr/libbufferhub/Android.mk b/libs/vr/libbufferhub/Android.mk
new file mode 100644
index 0000000..467f69f
--- /dev/null
+++ b/libs/vr/libbufferhub/Android.mk
@@ -0,0 +1,57 @@
+# 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_client.cpp \
+ buffer_hub_rpc.cpp \
+ ion_buffer.cpp
+
+includeFiles := \
+ $(LOCAL_PATH)/include
+
+staticLibraries := \
+ libchrome \
+ libdvrcommon \
+ libpdx_default_transport \
+
+sharedLibraries := \
+ libbase \
+ libcutils \
+ libhardware \
+ liblog \
+ libui \
+ libutils
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_CFLAGS := -DLOG_TAG=\"libbufferhub\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := libbufferhub
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := bufferhub_tests.cpp
+LOCAL_STATIC_LIBRARIES := libbufferhub $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := bufferhub_tests
+include $(BUILD_NATIVE_TEST)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp
new file mode 100644
index 0000000..146780e
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_client.cpp
@@ -0,0 +1,417 @@
+#include <private/dvr/buffer_hub_client.h>
+
+#include <cutils/log.h>
+#include <poll.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <mutex>
+
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/platform_defines.h>
+
+#include "include/private/dvr/bufferhub_rpc.h"
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::rpc::WrapBuffer;
+using android::pdx::Status;
+
+namespace {
+
+constexpr int kUncachedBlobUsageFlags = GRALLOC_USAGE_SW_READ_RARELY |
+ GRALLOC_USAGE_SW_WRITE_RARELY |
+ GRALLOC_USAGE_PRIVATE_UNCACHED;
+
+} // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+BufferHubBuffer::BufferHubBuffer(LocalChannelHandle channel_handle)
+ : Client{pdx::default_transport::ClientChannel::Create(
+ std::move(channel_handle))},
+ id_(-1) {}
+BufferHubBuffer::BufferHubBuffer(const std::string& endpoint_path)
+ : Client{pdx::default_transport::ClientChannelFactory::Create(
+ endpoint_path)},
+ id_(-1) {}
+
+BufferHubBuffer::~BufferHubBuffer() {}
+
+Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() {
+ Status<LocalChannelHandle> status =
+ InvokeRemoteMethod<BufferHubRPC::NewConsumer>();
+ ALOGE_IF(!status,
+ "BufferHub::CreateConsumer: Failed to create consumer channel: %s",
+ status.GetErrorMessage().c_str());
+ return status;
+}
+
+int BufferHubBuffer::ImportBuffer() {
+ ATRACE_NAME("BufferHubBuffer::ImportBuffer");
+ if (!IonBuffer::GetGrallocModule())
+ return -EIO;
+
+ Status<std::vector<NativeBufferHandle<LocalHandle>>> status =
+ InvokeRemoteMethod<BufferHubRPC::GetBuffers>();
+ if (!status) {
+ ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffers: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ } else if (status.get().empty()) {
+ ALOGE(
+ "BufferHubBuffer::ImportBuffer: Expected to receive at least one "
+ "buffer handle but got zero!");
+ return -EIO;
+ }
+
+ auto buffer_handles = status.take();
+
+ // Stash the buffer id to replace the value in id_. All sub-buffers of a
+ // buffer hub buffer have the same id.
+ const int new_id = buffer_handles[0].id();
+
+ // Import all of the buffers.
+ std::vector<IonBuffer> ion_buffers;
+ for (auto& handle : buffer_handles) {
+ const size_t i = &handle - buffer_handles.data();
+ ALOGD_IF(
+ TRACE,
+ "BufferHubBuffer::ImportBuffer: i=%zu id=%d FdCount=%zu IntCount=%zu",
+ i, handle.id(), handle.FdCount(), handle.IntCount());
+
+ IonBuffer buffer;
+ const int ret = handle.Import(&buffer);
+ if (ret < 0)
+ return ret;
+
+ ion_buffers.emplace_back(std::move(buffer));
+ }
+
+ // If all imports succeed, replace the previous buffers and id.
+ slices_ = std::move(ion_buffers);
+ id_ = new_id;
+ return 0;
+}
+
+int BufferHubBuffer::Poll(int timeout_ms) {
+ ATRACE_NAME("BufferHubBuffer::Poll");
+ pollfd p = {event_fd(), POLLIN, 0};
+ return poll(&p, 1, timeout_ms);
+}
+
+int BufferHubBuffer::Lock(int usage, int x, int y, int width, int height,
+ void** address, size_t index) {
+ return slices_[index].Lock(usage, x, y, width, height, address);
+}
+
+int BufferHubBuffer::Unlock(size_t index) { return slices_[index].Unlock(); }
+
+int BufferHubBuffer::GetBlobReadWritePointer(size_t size, void** addr) {
+ int width = static_cast<int>(size);
+ int height = 1;
+ constexpr int usage = GRALLOC_USAGE_SW_READ_RARELY |
+ GRALLOC_USAGE_SW_WRITE_RARELY |
+ GRALLOC_USAGE_PRIVATE_UNCACHED;
+ int ret = Lock(usage, 0, 0, width, height, addr);
+ if (ret == 0)
+ Unlock();
+ return ret;
+}
+
+int BufferHubBuffer::GetBlobReadOnlyPointer(size_t size, void** addr) {
+ int width = static_cast<int>(size);
+ int height = 1;
+ constexpr int usage =
+ GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_PRIVATE_UNCACHED;
+ int ret = Lock(usage, 0, 0, width, height, addr);
+ if (ret == 0)
+ Unlock();
+ return ret;
+}
+
+BufferConsumer::BufferConsumer(LocalChannelHandle channel)
+ : BASE(std::move(channel)) {
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE("BufferConsumer::BufferConsumer: Failed to import buffer: %s",
+ strerror(-ret));
+ Close(ret);
+ }
+}
+
+std::unique_ptr<BufferConsumer> BufferConsumer::Import(
+ LocalChannelHandle channel) {
+ ATRACE_NAME("BufferConsumer::Import");
+ ALOGD_IF(TRACE, "BufferConsumer::Import: channel=%d", channel.value());
+ return BufferConsumer::Create(std::move(channel));
+}
+
+std::unique_ptr<BufferConsumer> BufferConsumer::Import(
+ Status<LocalChannelHandle> status) {
+ return Import(status ? status.take()
+ : LocalChannelHandle{nullptr, -status.error()});
+}
+
+int BufferConsumer::Acquire(LocalHandle* ready_fence) {
+ return Acquire(ready_fence, nullptr, 0);
+}
+
+int BufferConsumer::Acquire(LocalHandle* ready_fence, void* meta,
+ size_t meta_size_bytes) {
+ ATRACE_NAME("BufferConsumer::Acquire");
+ LocalFence fence;
+ auto return_value =
+ std::make_pair(std::ref(fence), WrapBuffer(meta, meta_size_bytes));
+ auto status = InvokeRemoteMethodInPlace<BufferHubRPC::ConsumerAcquire>(
+ &return_value, meta_size_bytes);
+ if (status && ready_fence)
+ *ready_fence = fence.take();
+ return status ? 0 : -status.error();
+}
+
+int BufferConsumer::Release(const LocalHandle& release_fence) {
+ ATRACE_NAME("BufferConsumer::Release");
+ return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>(
+ BorrowedFence(release_fence.Borrow())));
+}
+
+int BufferConsumer::ReleaseAsync() {
+ ATRACE_NAME("BufferConsumer::ReleaseAsync");
+ return ReturnStatusOrError(
+ SendImpulse(BufferHubRPC::ConsumerRelease::Opcode));
+}
+
+int BufferConsumer::Discard() { return Release(LocalHandle()); }
+
+int BufferConsumer::SetIgnore(bool ignore) {
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(ignore));
+}
+
+BufferProducer::BufferProducer(int width, int height, int format, int usage,
+ size_t metadata_size, size_t slice_count)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("BufferProducer::BufferProducer");
+ ALOGD_IF(TRACE,
+ "BufferProducer::BufferProducer: fd=%d width=%d height=%d format=%d "
+ "usage=%d, metadata_size=%zu, slice_count=%zu",
+ event_fd(), width, height, format, usage, metadata_size,
+ slice_count);
+
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+ width, height, format, usage, metadata_size, slice_count);
+ if (!status) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to create producer buffer: %s",
+ status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+ strerror(-ret));
+ Close(ret);
+ }
+}
+
+BufferProducer::BufferProducer(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)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("BufferProducer::BufferProducer");
+ ALOGD_IF(TRACE,
+ "BufferProducer::BufferProducer: fd=%d name=%s user_id=%d "
+ "group_id=%d width=%d height=%d format=%d usage=%d, "
+ "meta_size_bytes=%zu, slice_count=%zu",
+ event_fd(), name.c_str(), user_id, group_id, width, height, format,
+ usage, meta_size_bytes, slice_count);
+
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
+ name, user_id, group_id, width, height, format, usage, meta_size_bytes,
+ slice_count);
+ if (!status) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to create/get persistent "
+ "buffer \"%s\": %s",
+ name.c_str(), status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer "
+ "\"%s\": %s",
+ name.c_str(), strerror(-ret));
+ Close(ret);
+ }
+}
+
+BufferProducer::BufferProducer(int usage, size_t size)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("BufferProducer::BufferProducer");
+ ALOGD_IF(TRACE, "BufferProducer::BufferProducer: usage=%d size=%zu", usage,
+ size);
+ const int width = static_cast<int>(size);
+ const int height = 1;
+ const int format = HAL_PIXEL_FORMAT_BLOB;
+ const size_t meta_size_bytes = 0;
+ const size_t slice_count = 1;
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+ width, height, format, usage, meta_size_bytes, slice_count);
+ if (!status) {
+ ALOGE("BufferProducer::BufferProducer: Failed to create blob: %s",
+ status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+ strerror(-ret));
+ Close(ret);
+ }
+}
+
+BufferProducer::BufferProducer(const std::string& name, int user_id,
+ int group_id, int usage, size_t size)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("BufferProducer::BufferProducer");
+ ALOGD_IF(TRACE,
+ "BufferProducer::BufferProducer: name=%s user_id=%d group=%d "
+ "usage=%d size=%zu",
+ name.c_str(), user_id, group_id, usage, size);
+ const int width = static_cast<int>(size);
+ const int height = 1;
+ const int format = HAL_PIXEL_FORMAT_BLOB;
+ const size_t meta_size_bytes = 0;
+ const size_t slice_count = 1;
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
+ name, user_id, group_id, width, height, format, usage, meta_size_bytes,
+ slice_count);
+ if (!status) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to create persistent "
+ "buffer \"%s\": %s",
+ name.c_str(), status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer "
+ "\"%s\": %s",
+ name.c_str(), strerror(-ret));
+ Close(ret);
+ }
+}
+
+BufferProducer::BufferProducer(const std::string& name)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("BufferProducer::BufferProducer");
+ ALOGD_IF(TRACE, "BufferProducer::BufferProducer: name=%s", name.c_str());
+
+ auto status = InvokeRemoteMethod<BufferHubRPC::GetPersistentBuffer>(name);
+ if (!status) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to get producer buffer by name "
+ "\"%s\": %s",
+ name.c_str(), status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer "
+ "\"%s\": %s",
+ name.c_str(), strerror(-ret));
+ Close(ret);
+ }
+}
+
+BufferProducer::BufferProducer(LocalChannelHandle channel)
+ : BASE(std::move(channel)) {
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+ strerror(-ret));
+ Close(ret);
+ }
+}
+
+int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta,
+ size_t meta_size_bytes) {
+ ATRACE_NAME("BufferProducer::Post");
+ return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>(
+ BorrowedFence(ready_fence.Borrow()), WrapBuffer(meta, meta_size_bytes)));
+}
+
+int BufferProducer::Gain(LocalHandle* release_fence) {
+ ATRACE_NAME("BufferProducer::Gain");
+ auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>();
+ if (!status)
+ return -status.error();
+ if (release_fence)
+ *release_fence = status.take().take();
+ return 0;
+}
+
+int BufferProducer::GainAsync() {
+ ATRACE_NAME("BufferProducer::GainAsync");
+ return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode));
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::Import(
+ LocalChannelHandle channel) {
+ ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value());
+ return BufferProducer::Create(std::move(channel));
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::Import(
+ Status<LocalChannelHandle> status) {
+ return Import(status ? status.take()
+ : LocalChannelHandle{nullptr, -status.error()});
+}
+
+int BufferProducer::MakePersistent(const std::string& name, int user_id,
+ int group_id) {
+ ATRACE_NAME("BufferProducer::MakePersistent");
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<BufferHubRPC::ProducerMakePersistent>(name, user_id,
+ group_id));
+}
+
+int BufferProducer::RemovePersistence() {
+ ATRACE_NAME("BufferProducer::RemovePersistence");
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<BufferHubRPC::ProducerRemovePersistence>());
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::CreateUncachedBlob(
+ size_t size) {
+ return BufferProducer::Create(kUncachedBlobUsageFlags, size);
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::CreatePersistentUncachedBlob(
+ const std::string& name, int user_id, int group_id, size_t size) {
+ return BufferProducer::Create(name, user_id, group_id,
+ kUncachedBlobUsageFlags, size);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhub/buffer_hub_rpc.cpp b/libs/vr/libbufferhub/buffer_hub_rpc.cpp
new file mode 100644
index 0000000..9a67faa
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_rpc.cpp
@@ -0,0 +1,9 @@
+#include "include/private/dvr/bufferhub_rpc.h"
+
+namespace android {
+namespace dvr {
+
+constexpr char BufferHubRPC::kClientPath[];
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhub/bufferhub_tests.cpp b/libs/vr/libbufferhub/bufferhub_tests.cpp
new file mode 100644
index 0000000..cb45dbe
--- /dev/null
+++ b/libs/vr/libbufferhub/bufferhub_tests.cpp
@@ -0,0 +1,219 @@
+#include <android/native_window.h>
+#include <base/posix/eintr_wrapper.h>
+#include <gtest/gtest.h>
+#include <private/dvr/buffer_hub_client.h>
+
+#include <mutex>
+#include <thread>
+
+using android::dvr::BufferProducer;
+using android::dvr::BufferConsumer;
+using android::pdx::LocalHandle;
+
+const int kWidth = 640;
+const int kHeight = 480;
+const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+const int kUsage = 0;
+const uint64_t kContext = 42;
+
+using LibBufferHubTest = ::testing::Test;
+
+TEST_F(LibBufferHubTest, TestBasicUsage) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+ // Check that consumers can spawn other consumers.
+ std::unique_ptr<BufferConsumer> c2 =
+ BufferConsumer::Import(c->CreateConsumer());
+ ASSERT_TRUE(c2.get() != nullptr);
+
+ EXPECT_EQ(0, p->Post(LocalHandle(), kContext));
+ // Both consumers should be triggered.
+ EXPECT_GE(0, HANDLE_EINTR(p->Poll(0)));
+ EXPECT_LT(0, HANDLE_EINTR(c->Poll(10)));
+ EXPECT_LT(0, HANDLE_EINTR(c2->Poll(10)));
+
+ uint64_t context;
+ LocalHandle fence;
+ EXPECT_LE(0, c->Acquire(&fence, &context));
+ EXPECT_EQ(kContext, context);
+ EXPECT_GE(0, HANDLE_EINTR(c->Poll(0)));
+
+ EXPECT_LE(0, c2->Acquire(&fence, &context));
+ EXPECT_EQ(kContext, context);
+ EXPECT_GE(0, HANDLE_EINTR(c2->Poll(0)));
+
+ EXPECT_EQ(0, c->Release(LocalHandle()));
+ EXPECT_GE(0, HANDLE_EINTR(p->Poll(0)));
+ EXPECT_EQ(0, c2->Discard());
+
+ EXPECT_LE(0, HANDLE_EINTR(p->Poll(0)));
+ EXPECT_EQ(0, p->Gain(&fence));
+ EXPECT_GE(0, HANDLE_EINTR(p->Poll(0)));
+}
+
+TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
+ struct Metadata {
+ int64_t field1;
+ int64_t field2;
+ };
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ Metadata m = {1, 3};
+ EXPECT_EQ(0, p->Post(LocalHandle(), m));
+ EXPECT_LE(0, HANDLE_EINTR(c->Poll(10)));
+
+ LocalHandle fence;
+ Metadata m2 = {};
+ EXPECT_EQ(0, c->Acquire(&fence, &m2));
+ EXPECT_EQ(m.field1, m2.field1);
+ EXPECT_EQ(m.field2, m2.field2);
+
+ EXPECT_EQ(0, c->Release(LocalHandle()));
+ EXPECT_LT(0, HANDLE_EINTR(p->Poll(0)));
+}
+
+TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) {
+ struct Metadata {
+ int64_t field1;
+ int64_t field2;
+ };
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ int64_t sequence = 3;
+ EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+ EXPECT_GE(0, HANDLE_EINTR(c->Poll(10)));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) {
+ struct Metadata {
+ int64_t field1;
+ int64_t field2;
+ };
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ Metadata m = {1, 3};
+ EXPECT_EQ(0, p->Post(LocalHandle(), m));
+
+ LocalHandle fence;
+ int64_t sequence;
+ EXPECT_NE(0, c->Acquire(&fence, &sequence));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireWithNoMeta) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ int64_t sequence = 3;
+ EXPECT_EQ(0, p->Post(LocalHandle(), sequence));
+
+ LocalHandle fence;
+ EXPECT_EQ(0, c->Acquire(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestWithNoMeta) {
+ std::unique_ptr<BufferProducer> p =
+ BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ LocalHandle fence;
+
+ EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+ EXPECT_EQ(0, c->Acquire(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestFailureToPostMetaFromABufferWithoutMeta) {
+ std::unique_ptr<BufferProducer> p =
+ BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ int64_t sequence = 3;
+ EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+}
+
+TEST_F(LibBufferHubTest, TestPersistentBufferPersistence) {
+ auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+ kHeight, kFormat, kUsage);
+ ASSERT_NE(nullptr, p);
+
+ // Record the original buffer id for later comparison.
+ const int buffer_id = p->id();
+
+ auto c = BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_NE(nullptr, c);
+
+ EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+
+ // Close the connection to the producer. This should not affect the consumer.
+ p = nullptr;
+
+ LocalHandle fence;
+ EXPECT_EQ(0, c->Acquire(&fence));
+ EXPECT_EQ(0, c->Release(LocalHandle()));
+
+ // Attempt to reconnect to the persistent buffer.
+ p = BufferProducer::Create("TestPersistentBuffer");
+ ASSERT_NE(nullptr, p);
+ EXPECT_EQ(buffer_id, p->id());
+ EXPECT_EQ(0, p->Gain(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestPersistentBufferMismatchParams) {
+ auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+ kHeight, kFormat, kUsage);
+ ASSERT_NE(nullptr, p);
+
+ // Close the connection to the producer.
+ p = nullptr;
+
+ // Mismatch the params.
+ p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth * 2,
+ kHeight, kFormat, kUsage);
+ ASSERT_EQ(nullptr, p);
+}
+
+TEST_F(LibBufferHubTest, TestRemovePersistentBuffer) {
+ auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+ kHeight, kFormat, kUsage);
+ ASSERT_NE(nullptr, p);
+
+ LocalHandle fence;
+ auto c = BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_NE(nullptr, c);
+ EXPECT_NE(-EPIPE, c->Acquire(&fence));
+
+ // Test that removing persistence and closing the producer orphans the
+ // consumer.
+ EXPECT_EQ(0, p->RemovePersistence());
+ p = nullptr;
+
+ EXPECT_EQ(-EPIPE, c->Release(LocalHandle()));
+}
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
new file mode 100644
index 0000000..b6ff5b6
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
@@ -0,0 +1,313 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_CLIENT_H_
+#define ANDROID_DVR_BUFFER_HUB_CLIENT_H_
+
+#include <hardware/gralloc.h>
+#include <pdx/channel_handle.h>
+#include <pdx/client.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+#include <vector>
+
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+class BufferHubBuffer : public pdx::Client {
+ public:
+ using LocalHandle = pdx::LocalHandle;
+ using LocalChannelHandle = pdx::LocalChannelHandle;
+ template <typename T>
+ using Status = pdx::Status<T>;
+
+ // Create a new consumer channel that is attached to the producer. Returns
+ // a file descriptor for the new channel or a negative error code.
+ Status<LocalChannelHandle> CreateConsumer();
+
+ // Polls the fd for |timeout_ms| milliseconds (-1 for infinity).
+ int Poll(int timeout_ms);
+
+ // Locks the area specified by (x, y, width, height) for a specific usage. If
+ // the usage is software then |addr| will be updated to point to the address
+ // of the buffer in virtual memory. The caller should only access/modify the
+ // pixels in the specified area. anything else is undefined behavior.
+ int Lock(int usage, int x, int y, int width, int height, void** addr,
+ size_t index);
+
+ // Must be called after Lock() when the caller has finished changing the
+ // buffer.
+ int Unlock(size_t index);
+
+ // Helper for when index is 0.
+ int Lock(int usage, int x, int y, int width, int height, void** addr) {
+ return Lock(usage, x, y, width, height, addr, 0);
+ }
+
+ // Helper for when index is 0.
+ int Unlock() { return Unlock(0); }
+
+ // Gets a blob buffer that was created with BufferProducer::CreateBlob.
+ // Locking and Unlocking is handled internally. There's no need to Unlock
+ // after calling this method.
+ int GetBlobReadWritePointer(size_t size, void** addr);
+
+ // Gets a blob buffer that was created with BufferProducer::CreateBlob.
+ // Locking and Unlocking is handled internally. There's no need to Unlock
+ // after calling this method.
+ int GetBlobReadOnlyPointer(size_t size, void** addr);
+
+ // Returns a dup'd file descriptor for accessing the blob shared memory. The
+ // caller takes ownership of the file descriptor and must close it or pass on
+ // ownership. Some GPU API extensions can take file descriptors to bind shared
+ // memory gralloc buffers to GPU buffer objects.
+ LocalHandle GetBlobFd() const {
+ // Current GPU vendor puts the buffer allocation in one FD. If we change GPU
+ // vendors and this is the wrong fd, late-latching and EDS will very clearly
+ // stop working and we will need to correct this. The alternative is to use
+ // a GL context in the pose service to allocate this buffer or to use the
+ // ION API directly instead of gralloc.
+ return LocalHandle(dup(native_handle()->data[0]));
+ }
+
+ using Client::event_fd;
+ native_handle_t* native_handle() const {
+ return const_cast<native_handle_t*>(slices_[0].handle());
+ }
+ // If index is greater than or equal to slice_count(), the result is
+ // undefined.
+ native_handle_t* native_handle(size_t index) const {
+ return const_cast<native_handle_t*>(slices_[index].handle());
+ }
+
+ IonBuffer* buffer() { return &slices_[0]; }
+ // If index is greater than or equal to slice_count(), the result is
+ // undefined.
+ IonBuffer* slice(size_t index) { return &slices_[index]; }
+
+ int slice_count() const { return static_cast<int>(slices_.size()); }
+ int id() const { return id_; }
+
+ // The following methods return settings of the first buffer. Currently,
+ // it is only possible to create multi-buffer BufferHubBuffers with the same
+ // settings.
+ int width() const { return slices_[0].width(); }
+ int height() const { return slices_[0].height(); }
+ int stride() const { return slices_[0].stride(); }
+ int format() const { return slices_[0].format(); }
+ int usage() const { return slices_[0].usage(); }
+
+ protected:
+ explicit BufferHubBuffer(LocalChannelHandle channel);
+ explicit BufferHubBuffer(const std::string& endpoint_path);
+ virtual ~BufferHubBuffer();
+
+ // Initialization helper.
+ int ImportBuffer();
+
+ private:
+ BufferHubBuffer(const BufferHubBuffer&) = delete;
+ void operator=(const BufferHubBuffer&) = delete;
+
+ // Global id for the buffer that is consistent across processes. It is meant
+ // for logging and debugging purposes only and should not be used for lookup
+ // or any other functional purpose as a security precaution.
+ int id_;
+
+ // A BufferHubBuffer may contain multiple slices of IonBuffers with same
+ // configurations.
+ std::vector<IonBuffer> slices_;
+};
+
+// This represents a writable buffer. Calling Post notifies all clients and
+// makes the buffer read-only. Call Gain to acquire write access. A buffer
+// may have many consumers.
+//
+// The user of BufferProducer is responsible with making sure that the Post() is
+// done with the correct metadata type and size. The user is also responsible
+// for making sure that remote ends (BufferConsumers) are also using the correct
+// metadata when acquiring the buffer. The API guarantees that a Post() with a
+// metadata of wrong size will fail. However, it currently does not do any
+// type checking.
+// The API also assumes that metadata is a serializable type (plain old data).
+class BufferProducer : public pdx::ClientBase<BufferProducer, BufferHubBuffer> {
+ public:
+ // Create a buffer designed to hold arbitrary bytes that can be read and
+ // written from CPU, GPU and DSP. The buffer is mapped uncached so that CPU
+ // reads and writes are predictable.
+ static std::unique_ptr<BufferProducer> CreateUncachedBlob(size_t size);
+
+ // Creates a persistent uncached buffer with the given name and access.
+ static std::unique_ptr<BufferProducer> CreatePersistentUncachedBlob(
+ const std::string& name, int user_id, int group_id, size_t size);
+
+ // Imports a bufferhub producer channel, assuming ownership of its handle.
+ static std::unique_ptr<BufferProducer> Import(LocalChannelHandle channel);
+ static std::unique_ptr<BufferProducer> Import(
+ Status<LocalChannelHandle> status);
+
+ // Post this buffer, passing |ready_fence| to the consumers. The bytes in
+ // |meta| are passed unaltered to the consumers. The producer must not modify
+ // the buffer until it is re-gained.
+ // This returns zero or a negative unix error code.
+ int Post(const LocalHandle& ready_fence, const void* meta,
+ size_t meta_size_bytes);
+
+ template <typename Meta,
+ typename = typename std::enable_if<std::is_void<Meta>::value>::type>
+ int Post(const LocalHandle& ready_fence) {
+ return Post(ready_fence, nullptr, 0);
+ }
+ template <typename Meta, typename = typename std::enable_if<
+ !std::is_void<Meta>::value>::type>
+ int Post(const LocalHandle& ready_fence, const Meta& meta) {
+ return Post(ready_fence, &meta, sizeof(meta));
+ }
+
+ // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it
+ // must be waited on before using the buffer. If it is not valid then the
+ // buffer is free for immediate use. This call will only succeed if the buffer
+ // is in the released state.
+ // This returns zero or a negative unix error code.
+ int Gain(LocalHandle* release_fence);
+
+ // Asynchronously marks a released buffer as gained. This method is similar to
+ // the synchronous version above, except that it does not wait for BufferHub
+ // to acknowledge success or failure, nor does it transfer a release fence to
+ // the client. This version may be used in situations where a release fence is
+ // not needed. Because of the asynchronous nature of the underlying message,
+ // no error is returned if this method is called when the buffer is in an
+ // incorrect state. Returns zero if sending the message succeeded, or a
+ // negative errno code otherwise.
+ int GainAsync();
+
+ // Attaches the producer to |name| so that it becomes a persistent buffer that
+ // may be retrieved by name at a later time. This may be used in cases where a
+ // shared memory buffer should persist across the life of the producer process
+ // (i.e. the buffer may be held by clients across a service restart). The
+ // buffer may be associated with a user and/or group id to restrict access to
+ // the buffer. If user_id or group_id is -1 then checks for the respective id
+ // are disabled. If user_id or group_id is 0 then the respective id of the
+ // calling process is used instead.
+ int MakePersistent(const std::string& name, int user_id, int group_id);
+
+ // Removes the persistence of the producer.
+ int RemovePersistence();
+
+ private:
+ friend BASE;
+
+ // Constructors are automatically exposed through BufferProducer::Create(...)
+ // static template methods inherited from ClientBase, which take the same
+ // arguments as the constructors.
+
+ // Constructs a buffer with the given geometry and parameters.
+ BufferProducer(int width, int height, int format, int usage,
+ size_t metadata_size = 0, size_t slice_count = 1);
+
+ // Constructs a persistent buffer with the given geometry and parameters and
+ // binds it to |name| in one shot. If a persistent buffer with the same name
+ // and settings already exists and matches the given geometry and parameters,
+ // that buffer is connected to this client instead of creating a new buffer.
+ // If the name matches but the geometry or settings do not match then
+ // construction fails and BufferProducer::Create() returns nullptr.
+ //
+ // Access to the persistent buffer may be restricted by |user_id| and/or
+ // |group_id|; these settings are established only when the buffer is first
+ // created and cannot be changed. A user or group id of -1 disables checks for
+ // that respective id. A user or group id of 0 is substituted with the
+ // effective user or group id of the calling process.
+ BufferProducer(const std::string& name, int user_id, int group_id, int width,
+ int height, int format, int usage, size_t metadata_size = 0,
+ size_t slice_count = 1);
+
+ // Constructs a blob (flat) buffer with the given usage flags.
+ BufferProducer(int usage, size_t size);
+
+ // Constructs a persistent blob (flat) buffer and binds it to |name|.
+ BufferProducer(const std::string& name, int user_id, int group_id, int usage,
+ size_t size);
+
+ // Constructs a channel to persistent buffer by name only. The buffer must
+ // have been previously created or made persistent.
+ explicit BufferProducer(const std::string& name);
+
+ // Imports the given file handle to a producer channel, taking ownership.
+ explicit BufferProducer(LocalChannelHandle channel);
+};
+
+// This is a connection to a producer buffer, which can be located in another
+// application. When that buffer is Post()ed, this fd will be signaled and
+// Acquire allows read access. The user is responsible for making sure that
+// Acquire is called with the correct metadata structure. The only guarantee the
+// API currently provides is that an Acquire() with metadata of the wrong size
+// will fail.
+class BufferConsumer : public pdx::ClientBase<BufferConsumer, BufferHubBuffer> {
+ public:
+ // This call assumes ownership of |fd|.
+ static std::unique_ptr<BufferConsumer> Import(LocalChannelHandle channel);
+ static std::unique_ptr<BufferConsumer> Import(
+ Status<LocalChannelHandle> status);
+
+ // Attempt to retrieve a post event from buffer hub. If successful,
+ // |ready_fence| will be set to a fence to wait on until the buffer is ready.
+ // This call will only succeed after the fd is signalled. This call may be
+ // performed as an alternative to the Acquire() with metadata. In such cases
+ // the metadata is not read.
+ //
+ // This returns zero or negative unix error code.
+ int Acquire(LocalHandle* ready_fence);
+
+ // Attempt to retrieve a post event from buffer hub. If successful,
+ // |ready_fence| is set to a fence signaling that the contents of the buffer
+ // are available. This call will only succeed if the buffer is in the posted
+ // state.
+ // Returns zero on success, or a negative errno code otherwise.
+ int Acquire(LocalHandle* ready_fence, void* meta, size_t meta_size_bytes);
+
+ // Attempt to retrieve a post event from buffer hub. If successful,
+ // |ready_fence| is set to a fence to wait on until the buffer is ready. This
+ // call will only succeed after the fd is signaled. This returns zero or a
+ // negative unix error code.
+ template <typename Meta>
+ int Acquire(LocalHandle* ready_fence, Meta* meta) {
+ return Acquire(ready_fence, meta, sizeof(*meta));
+ }
+
+ // This should be called after a successful Acquire call. If the fence is
+ // valid the fence determines the buffer usage, otherwise the buffer is
+ // released immediately.
+ // This returns zero or a negative unix error code.
+ int Release(const LocalHandle& release_fence);
+
+ // Asynchronously releases a buffer. Similar to the synchronous version above,
+ // except that it does not wait for BufferHub to reply with success or error,
+ // nor does it transfer a release fence. This version may be used in
+ // situations where a release fence is not needed. Because of the asynchronous
+ // nature of the underlying message, no error is returned if this method is
+ // called when the buffer is in an incorrect state. Returns zero if sending
+ // the message succeeded, or a negative errno code otherwise.
+ int ReleaseAsync();
+
+ // May be called after or instead of Acquire to indicate that the consumer
+ // does not need to access the buffer this cycle. This returns zero or a
+ // negative unix error code.
+ int Discard();
+
+ // When set, this consumer is no longer notified when this buffer is
+ // available. The system behaves as if Discard() is immediately called
+ // whenever the buffer is posted. If ignore is set to true while a buffer is
+ // pending, it will act as if Discard() was also called.
+ // This returns zero or a negative unix error code.
+ int SetIgnore(bool ignore);
+
+ private:
+ friend BASE;
+
+ explicit BufferConsumer(LocalChannelHandle channel);
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFER_HUB_CLIENT_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
new file mode 100644
index 0000000..ed11551
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
@@ -0,0 +1,214 @@
+#ifndef ANDROID_DVR_BUFFERHUB_RPC_H_
+#define ANDROID_DVR_BUFFERHUB_RPC_H_
+
+#include <cutils/native_handle.h>
+#include <gui/BufferQueueDefs.h>
+#include <sys/types.h>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+template <typename FileHandleType>
+class NativeBufferHandle {
+ public:
+ NativeBufferHandle() { Clear(); }
+ NativeBufferHandle(const IonBuffer& buffer, int id)
+ : id_(id),
+ stride_(buffer.stride()),
+ width_(buffer.width()),
+ height_(buffer.height()),
+ format_(buffer.format()),
+ usage_(buffer.usage()) {
+ // Populate the fd and int vectors: native_handle->data[] is an array of fds
+ // followed by an array of opaque ints.
+ const int fd_count = buffer.handle()->numFds;
+ const int int_count = buffer.handle()->numInts;
+ for (int i = 0; i < fd_count; i++) {
+ fds_.emplace_back(FileHandleType::AsDuplicate(buffer.handle()->data[i]));
+ }
+ for (int i = 0; i < int_count; i++) {
+ opaque_ints_.push_back(buffer.handle()->data[fd_count + i]);
+ }
+ }
+ NativeBufferHandle(NativeBufferHandle&& other) = default;
+
+ // Imports the native handle into the given IonBuffer instance.
+ int Import(IonBuffer* buffer) {
+ // This is annoying, but we need to convert the vector of FileHandles into a
+ // vector of ints for the Import API.
+ std::vector<int> fd_ints;
+ for (const auto& fd : fds_)
+ fd_ints.push_back(fd.Get());
+
+ const int ret = buffer->Import(fd_ints.data(), fd_ints.size(),
+ opaque_ints_.data(), opaque_ints_.size(),
+ width_, height_, stride_, format_, usage_);
+ if (ret < 0)
+ return ret;
+
+ // Import succeeded, release the file handles which are now owned by the
+ // IonBuffer and clear members.
+ for (auto& fd : fds_)
+ fd.Release();
+ opaque_ints_.clear();
+ Clear();
+
+ return 0;
+ }
+
+ int id() const { return id_; }
+ size_t IntCount() const { return opaque_ints_.size(); }
+ size_t FdCount() const { return fds_.size(); }
+
+ private:
+ int id_;
+ int stride_;
+ int width_;
+ int height_;
+ int format_;
+ int usage_;
+ std::vector<int> opaque_ints_;
+ std::vector<FileHandleType> fds_;
+
+ void Clear() { id_ = stride_ = width_ = height_ = format_ = usage_ = -1; }
+
+ PDX_SERIALIZABLE_MEMBERS(NativeBufferHandle<FileHandleType>, id_, stride_,
+ width_, height_, format_, usage_, opaque_ints_,
+ fds_);
+
+ NativeBufferHandle(const NativeBufferHandle&) = delete;
+ void operator=(const NativeBufferHandle&) = delete;
+};
+
+template <typename FileHandleType>
+class FenceHandle {
+ public:
+ FenceHandle() = default;
+ explicit FenceHandle(int fence) : fence_{fence} {}
+ explicit FenceHandle(FileHandleType&& fence) : fence_{std::move(fence)} {}
+ FenceHandle(FenceHandle&&) = default;
+ FenceHandle& operator=(FenceHandle&&) = default;
+
+ explicit operator bool() const { return fence_.IsValid(); }
+
+ const FileHandleType& get() const { fence_; }
+ FileHandleType&& take() { return std::move(fence_); }
+
+ int get_fd() const { return fence_.Get(); }
+ void close() { fence_.Close(); }
+
+ FenceHandle<pdx::BorrowedHandle> borrow() const {
+ return FenceHandle<pdx::BorrowedHandle>(fence_.Borrow());
+ }
+
+ private:
+ FileHandleType fence_;
+
+ PDX_SERIALIZABLE_MEMBERS(FenceHandle<FileHandleType>, fence_);
+
+ FenceHandle(const FenceHandle&) = delete;
+ void operator=(const FenceHandle&) = delete;
+};
+
+using LocalFence = FenceHandle<pdx::LocalHandle>;
+using BorrowedFence = FenceHandle<pdx::BorrowedHandle>;
+
+// BufferHub Service RPC interface. Defines the endpoints, op codes, and method
+// type signatures supported by bufferhubd.
+struct BufferHubRPC {
+ // Service path.
+ static constexpr char kClientPath[] = "system/buffer_hub/client";
+
+ // |BufferHubQueue| will keep track of at most this value of buffers.
+ // Attempts at runtime to increase the number of buffers past this
+ // will fail. Note that the value is in sync with |android::BufferQueue|, so
+ // that slot id can be shared between |android::dvr::BufferHubQueueProducer|
+ // and |android::BufferQueueProducer| which both implements the same
+ // interface: |android::IGraphicBufferProducer|.
+ static constexpr size_t kMaxQueueCapacity =
+ android::BufferQueueDefs::NUM_BUFFER_SLOTS;
+
+ // Op codes.
+ enum {
+ kOpCreateBuffer = 0,
+ kOpCreatePersistentBuffer,
+ kOpGetPersistentBuffer,
+ kOpGetBuffer,
+ kOpGetBuffers,
+ kOpNewConsumer,
+ kOpProducerMakePersistent,
+ kOpProducerRemovePersistence,
+ kOpProducerPost,
+ kOpProducerGain,
+ kOpConsumerAcquire,
+ kOpConsumerRelease,
+ kOpConsumerSetIgnore,
+ kOpCreateProducerQueue,
+ kOpCreateConsumerQueue,
+ kOpProducerQueueAllocateBuffers,
+ kOpProducerQueueDetachBuffer,
+ kOpConsumerQueueImportBuffers,
+ };
+
+ // Aliases.
+ using MetaData = pdx::rpc::BufferWrapper<std::uint8_t*>;
+ using LocalChannelHandle = pdx::LocalChannelHandle;
+ using LocalHandle = pdx::LocalHandle;
+ using Void = pdx::rpc::Void;
+
+ // Methods.
+ PDX_REMOTE_METHOD(CreateBuffer, kOpCreateBuffer,
+ int(int width, int height, int format, int usage,
+ size_t meta_size_bytes, size_t slice_count));
+ PDX_REMOTE_METHOD(CreatePersistentBuffer, kOpCreatePersistentBuffer,
+ int(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));
+ PDX_REMOTE_METHOD(GetPersistentBuffer, kOpGetPersistentBuffer,
+ int(const std::string& name));
+ PDX_REMOTE_METHOD(GetBuffer, kOpGetBuffer,
+ NativeBufferHandle<LocalHandle>(unsigned index));
+ PDX_REMOTE_METHOD(GetBuffers, kOpGetBuffers,
+ std::vector<NativeBufferHandle<LocalHandle>>(Void));
+ PDX_REMOTE_METHOD(NewConsumer, kOpNewConsumer, LocalChannelHandle(Void));
+ PDX_REMOTE_METHOD(ProducerMakePersistent, kOpProducerMakePersistent,
+ int(const std::string& name, int user_id, int group_id));
+ PDX_REMOTE_METHOD(ProducerRemovePersistence, kOpProducerRemovePersistence,
+ int(Void));
+ PDX_REMOTE_METHOD(ProducerPost, kOpProducerPost,
+ int(LocalFence acquire_fence, MetaData));
+ PDX_REMOTE_METHOD(ProducerGain, kOpProducerGain, LocalFence(Void));
+ PDX_REMOTE_METHOD(ConsumerAcquire, kOpConsumerAcquire,
+ std::pair<LocalFence, MetaData>(std::size_t metadata_size));
+ PDX_REMOTE_METHOD(ConsumerRelease, kOpConsumerRelease,
+ int(LocalFence release_fence));
+ PDX_REMOTE_METHOD(ConsumerSetIgnore, kOpConsumerSetIgnore, int(bool ignore));
+
+ // Buffer Queue Methods.
+ PDX_REMOTE_METHOD(CreateProducerQueue, kOpCreateProducerQueue,
+ int(size_t meta_size_bytes, int usage_set_mask,
+ int usage_clear_mask, int usage_deny_set_mask,
+ int usage_deny_clear_mask));
+ PDX_REMOTE_METHOD(CreateConsumerQueue, kOpCreateConsumerQueue,
+ std::pair<LocalChannelHandle, size_t>(Void));
+ PDX_REMOTE_METHOD(ProducerQueueAllocateBuffers,
+ kOpProducerQueueAllocateBuffers,
+ std::vector<std::pair<LocalChannelHandle, size_t>>(
+ int width, int height, int format, int usage,
+ size_t slice_count, size_t buffer_count));
+ PDX_REMOTE_METHOD(ProducerQueueDetachBuffer, kOpProducerQueueDetachBuffer,
+ int(size_t slot));
+ PDX_REMOTE_METHOD(ConsumerQueueImportBuffers, kOpConsumerQueueImportBuffers,
+ std::vector<std::pair<LocalChannelHandle, size_t>>(Void));
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFERHUB_RPC_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
new file mode 100644
index 0000000..8125c54
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
@@ -0,0 +1,108 @@
+#ifndef ANDROID_DVR_ION_BUFFER_H_
+#define ANDROID_DVR_ION_BUFFER_H_
+
+#include <hardware/gralloc.h>
+
+namespace android {
+namespace dvr {
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class IonBuffer {
+ public:
+ IonBuffer();
+ IonBuffer(int width, int height, int format, int usage);
+ IonBuffer(buffer_handle_t handle, int width, int height, int stride,
+ int format, int usage);
+ IonBuffer(buffer_handle_t handle, int width, int height, int layer_count,
+ int stride, int layer_stride, int format, int usage);
+ ~IonBuffer();
+
+ IonBuffer(IonBuffer&& other);
+ IonBuffer& operator=(IonBuffer&& other);
+
+ // Frees the underlying native handle and leaves the instance initialized to
+ // empty.
+ void FreeHandle();
+
+ // Allocates a new native handle with the given parameters, freeing the
+ // previous native handle if necessary. Returns 0 on success or a negative
+ // errno code otherwise. If allocation fails the previous native handle is
+ // left intact.
+ int Alloc(int width, int height, int format, int usage);
+
+ // Resets the underlying native handle and parameters, freeing the previous
+ // native handle if necessary.
+ void Reset(buffer_handle_t handle, int width, int height, int stride,
+ int format, int usage);
+
+ // Like Reset but also registers the native handle, which is necessary for
+ // native handles received over IPC. Returns 0 on success or a negative errno
+ // code otherwise. If import fails the previous native handle is left intact.
+ int Import(buffer_handle_t handle, int width, int height, int stride,
+ int format, int usage);
+
+ // Like Reset but imports a native handle from raw fd and int arrays. Returns
+ // 0 on success or a negative errno code otherwise. If import fails the
+ // previous native handle is left intact.
+ int Import(const int* fd_array, int fd_count, const int* int_array,
+ int int_count, int width, int height, int stride, int format,
+ int usage);
+
+ // Duplicates the native handle underlying |other| and then imports it. This
+ // is useful for creating multiple, independent views of the same Ion/Gralloc
+ // buffer. Returns 0 on success or a negative errno code otherwise. If
+ // duplication or import fail the previous native handle is left intact.
+ int Duplicate(const IonBuffer* other);
+
+ int Lock(int usage, int x, int y, int width, int height, void** address);
+ int LockYUV(int usage, int x, int y, int width, int height,
+ struct android_ycbcr* yuv);
+ int Unlock();
+
+ buffer_handle_t handle() const { return handle_; }
+ int width() const { return width_; }
+ int height() const { return height_; }
+ int layer_count() const { return layer_count_; }
+ int stride() const { return stride_; }
+ int layer_stride() const { return layer_stride_; }
+ int format() const { return format_; }
+ int usage() const { return usage_; }
+
+ static gralloc_module_t const* GetGrallocModule() {
+ GrallocInit();
+ return gralloc_module_;
+ }
+
+ static alloc_device_t* GetGrallocDevice() {
+ GrallocInit();
+ return gralloc_device_;
+ }
+
+ private:
+ buffer_handle_t handle_;
+ int width_;
+ int height_;
+ int layer_count_;
+ int stride_;
+ int layer_stride_;
+ int format_;
+ int usage_;
+ bool locked_;
+ bool needs_unregister_;
+
+ void Replace(buffer_handle_t handle, int width, int height, int layer_count,
+ int stride, int layer_stride, int format, int usage,
+ bool needs_unregister);
+
+ static void GrallocInit();
+ static gralloc_module_t const* gralloc_module_;
+ static alloc_device_t* gralloc_device_;
+
+ IonBuffer(const IonBuffer&) = delete;
+ void operator=(const IonBuffer&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_ION_BUFFER_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/native_buffer.h b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
new file mode 100644
index 0000000..f6c24d9
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
@@ -0,0 +1,226 @@
+#ifndef ANDROID_DVR_NATIVE_BUFFER_H_
+#define ANDROID_DVR_NATIVE_BUFFER_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <android/native_window.h>
+#include <base/logging.h>
+#include <cutils/log.h>
+#include <system/window.h>
+#include <ui/ANativeObjectBase.h>
+#include <utils/RefBase.h>
+
+#include <private/dvr/buffer_hub_client.h>
+
+namespace android {
+namespace dvr {
+
+// ANativeWindowBuffer is the abstraction Android HALs and frameworks use to
+// pass around hardware graphics buffers. The following classes implement this
+// abstraction with different DVR backing buffers, all of which provide
+// different semantics on top of ion/gralloc buffers.
+
+// An implementation of ANativeWindowBuffer backed by an IonBuffer.
+class NativeBuffer
+ : public android::ANativeObjectBase<ANativeWindowBuffer, NativeBuffer,
+ android::LightRefBase<NativeBuffer>> {
+ public:
+ static constexpr int kEmptyFence = -1;
+
+ explicit NativeBuffer(const std::shared_ptr<IonBuffer>& buffer)
+ : BASE(), buffer_(buffer), fence_(kEmptyFence) {
+ ANativeWindowBuffer::width = buffer->width();
+ ANativeWindowBuffer::height = buffer->height();
+ ANativeWindowBuffer::stride = buffer->stride();
+ ANativeWindowBuffer::format = buffer->format();
+ ANativeWindowBuffer::usage = buffer->usage();
+ handle = buffer_->handle();
+ }
+
+ virtual ~NativeBuffer() {}
+
+ std::shared_ptr<IonBuffer> buffer() { return buffer_; }
+ int fence() const { return fence_.Get(); }
+
+ void SetFence(int fence) { fence_.Reset(fence); }
+
+ private:
+ friend class android::LightRefBase<NativeBuffer>;
+
+ std::shared_ptr<IonBuffer> buffer_;
+ pdx::LocalHandle fence_;
+
+ NativeBuffer(const NativeBuffer&) = delete;
+ void operator=(NativeBuffer&) = delete;
+};
+
+// NativeBufferProducerSlice is an implementation of ANativeWindowBuffer backed
+// by a buffer slice of a BufferProducer.
+class NativeBufferProducerSlice
+ : public android::ANativeObjectBase<
+ ANativeWindowBuffer, NativeBufferProducerSlice,
+ android::LightRefBase<NativeBufferProducerSlice>> {
+ public:
+ NativeBufferProducerSlice(const std::shared_ptr<BufferProducer>& buffer,
+ int buffer_index)
+ : BASE(), buffer_(buffer) {
+ ANativeWindowBuffer::width = buffer_->width();
+ ANativeWindowBuffer::height = buffer_->height();
+ ANativeWindowBuffer::stride = buffer_->stride();
+ ANativeWindowBuffer::format = buffer_->format();
+ ANativeWindowBuffer::usage = buffer_->usage();
+ handle = buffer_->native_handle(buffer_index);
+ }
+
+ virtual ~NativeBufferProducerSlice() {}
+
+ private:
+ friend class android::LightRefBase<NativeBufferProducerSlice>;
+
+ std::shared_ptr<BufferProducer> buffer_;
+
+ NativeBufferProducerSlice(const NativeBufferProducerSlice&) = delete;
+ void operator=(NativeBufferProducerSlice&) = delete;
+};
+
+// NativeBufferProducer is an implementation of ANativeWindowBuffer backed by a
+// BufferProducer.
+class NativeBufferProducer : public android::ANativeObjectBase<
+ ANativeWindowBuffer, NativeBufferProducer,
+ android::LightRefBase<NativeBufferProducer>> {
+ public:
+ static constexpr int kEmptyFence = -1;
+
+ NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer,
+ EGLDisplay display, uint32_t surface_buffer_index)
+ : BASE(),
+ buffer_(buffer),
+ surface_buffer_index_(surface_buffer_index),
+ display_(display) {
+ ANativeWindowBuffer::width = buffer_->width();
+ ANativeWindowBuffer::height = buffer_->height();
+ ANativeWindowBuffer::stride = buffer_->stride();
+ ANativeWindowBuffer::format = buffer_->format();
+ ANativeWindowBuffer::usage = buffer_->usage();
+ handle = buffer_->native_handle();
+ for (int i = 0; i < buffer->slice_count(); ++i) {
+ // display == null means don't create an EGL image. This is used by our
+ // Vulkan code.
+ slices_.push_back(new NativeBufferProducerSlice(buffer, i));
+ if (display_ != nullptr) {
+ egl_images_.push_back(eglCreateImageKHR(
+ display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ static_cast<ANativeWindowBuffer*>(slices_.back().get()), nullptr));
+ if (egl_images_.back() == EGL_NO_IMAGE_KHR) {
+ ALOGE("NativeBufferProducer: eglCreateImageKHR failed");
+ }
+ }
+ }
+ }
+
+ explicit NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer)
+ : NativeBufferProducer(buffer, nullptr, 0) {}
+
+ virtual ~NativeBufferProducer() {
+ for (EGLImageKHR egl_image : egl_images_) {
+ if (egl_image != EGL_NO_IMAGE_KHR)
+ eglDestroyImageKHR(display_, egl_image);
+ }
+ }
+
+ EGLImageKHR image_khr(int index) const { return egl_images_[index]; }
+ std::shared_ptr<BufferProducer> buffer() const { return buffer_; }
+ int release_fence() const { return release_fence_.Get(); }
+ uint32_t surface_buffer_index() const { return surface_buffer_index_; }
+
+ // Return the release fence, passing ownership to the caller.
+ pdx::LocalHandle ClaimReleaseFence() { return std::move(release_fence_); }
+
+ // Post the buffer consumer, closing the acquire and release fences.
+ int Post(int acquire_fence, uint64_t sequence) {
+ release_fence_.Close();
+ return buffer_->Post(pdx::LocalHandle(acquire_fence), sequence);
+ }
+
+ // Gain the buffer producer, closing the previous release fence if valid.
+ int Gain() { return buffer_->Gain(&release_fence_); }
+
+ // Asynchronously gain the buffer, closing the previous release fence.
+ int GainAsync() {
+ release_fence_.Close();
+ return buffer_->GainAsync();
+ }
+
+ private:
+ friend class android::LightRefBase<NativeBufferProducer>;
+
+ std::shared_ptr<BufferProducer> buffer_;
+ pdx::LocalHandle release_fence_;
+ std::vector<android::sp<NativeBufferProducerSlice>> slices_;
+ std::vector<EGLImageKHR> egl_images_;
+ uint32_t surface_buffer_index_;
+ EGLDisplay display_;
+
+ NativeBufferProducer(const NativeBufferProducer&) = delete;
+ void operator=(NativeBufferProducer&) = delete;
+};
+
+// NativeBufferConsumer is an implementation of ANativeWindowBuffer backed by a
+// BufferConsumer.
+class NativeBufferConsumer : public android::ANativeObjectBase<
+ ANativeWindowBuffer, NativeBufferConsumer,
+ android::LightRefBase<NativeBufferConsumer>> {
+ public:
+ static constexpr int kEmptyFence = -1;
+
+ explicit NativeBufferConsumer(const std::shared_ptr<BufferConsumer>& buffer,
+ int index)
+ : BASE(), buffer_(buffer), acquire_fence_(kEmptyFence), sequence_(0) {
+ ANativeWindowBuffer::width = buffer_->width();
+ ANativeWindowBuffer::height = buffer_->height();
+ ANativeWindowBuffer::stride = buffer_->stride();
+ ANativeWindowBuffer::format = buffer_->format();
+ ANativeWindowBuffer::usage = buffer_->usage();
+ CHECK(buffer_->slice_count() > index);
+ handle = buffer_->slice(index)->handle();
+ }
+
+ explicit NativeBufferConsumer(const std::shared_ptr<BufferConsumer>& buffer)
+ : NativeBufferConsumer(buffer, 0) {}
+
+ virtual ~NativeBufferConsumer() {}
+
+ std::shared_ptr<BufferConsumer> buffer() const { return buffer_; }
+ int acquire_fence() const { return acquire_fence_.Get(); }
+ uint64_t sequence() const { return sequence_; }
+
+ // Return the acquire fence, passing ownership to the caller.
+ pdx::LocalHandle ClaimAcquireFence() { return std::move(acquire_fence_); }
+
+ // Acquire the underlying buffer consumer, closing the previous acquire fence
+ // if valid.
+ int Acquire() { return buffer_->Acquire(&acquire_fence_, &sequence_); }
+
+ // Release the buffer consumer, closing the acquire and release fences if
+ // valid.
+ int Release(int release_fence) {
+ acquire_fence_.Close();
+ sequence_ = 0;
+ return buffer_->Release(pdx::LocalHandle(release_fence));
+ }
+
+ private:
+ friend class android::LightRefBase<NativeBufferConsumer>;
+
+ std::shared_ptr<BufferConsumer> buffer_;
+ pdx::LocalHandle acquire_fence_;
+ uint64_t sequence_;
+
+ NativeBufferConsumer(const NativeBufferConsumer&) = delete;
+ void operator=(NativeBufferConsumer&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_NATIVE_BUFFER_H_
diff --git a/libs/vr/libbufferhub/ion_buffer.cpp b/libs/vr/libbufferhub/ion_buffer.cpp
new file mode 100644
index 0000000..7d20049
--- /dev/null
+++ b/libs/vr/libbufferhub/ion_buffer.cpp
@@ -0,0 +1,322 @@
+#include <private/dvr/ion_buffer.h>
+
+#include <cutils/log.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <mutex>
+
+namespace android {
+namespace dvr {
+
+gralloc_module_t const* IonBuffer::gralloc_module_ = nullptr;
+alloc_device_t* IonBuffer::gralloc_device_ = nullptr;
+
+IonBuffer::IonBuffer() : IonBuffer(nullptr, 0, 0, 0, 0, 0, 0, 0) {}
+
+IonBuffer::IonBuffer(int width, int height, int format, int usage)
+ : IonBuffer() {
+ Alloc(width, height, format, usage);
+}
+
+IonBuffer::IonBuffer(buffer_handle_t handle, int width, int height, int stride,
+ int format, int usage)
+ : IonBuffer(handle, width, height, 1, stride, 0, format, usage) {}
+
+IonBuffer::IonBuffer(buffer_handle_t handle, int width, int height,
+ int layer_count, int stride, int layer_stride, int format,
+ int usage)
+ : handle_(handle),
+ width_(width),
+ height_(height),
+ layer_count_(layer_count),
+ stride_(stride),
+ layer_stride_(layer_stride),
+ format_(format),
+ usage_(usage),
+ locked_(false),
+ needs_unregister_(false) {
+ ALOGD_IF(TRACE,
+ "IonBuffer::IonBuffer: handle=%p width=%d height=%d layer_count=%d "
+ "stride=%d layer stride=%d format=%d usage=%d",
+ handle_, width_, height_, layer_count_, stride_, layer_stride_,
+ format_, usage_);
+ GrallocInit();
+}
+
+IonBuffer::~IonBuffer() {
+ ALOGD_IF(TRACE,
+ "IonBuffer::~IonBuffer: handle=%p width=%d height=%d stride=%d "
+ "format=%d usage=%d",
+ handle_, width_, height_, stride_, format_, usage_);
+
+ FreeHandle();
+}
+
+IonBuffer::IonBuffer(IonBuffer&& other) : IonBuffer() {
+ *this = std::move(other);
+}
+
+IonBuffer& IonBuffer::operator=(IonBuffer&& other) {
+ ALOGD_IF(TRACE, "IonBuffer::operator=: handle_=%p other.handle_=%p", handle_,
+ other.handle_);
+
+ if (this != &other) {
+ Replace(other.handle_, other.width_, other.height_, other.layer_count_,
+ other.stride_, other.layer_stride_, other.format_, other.usage_,
+ other.needs_unregister_);
+ locked_ = other.locked_;
+ other.handle_ = nullptr;
+ other.FreeHandle();
+ }
+
+ return *this;
+}
+
+void IonBuffer::FreeHandle() {
+ if (handle_) {
+ // Lock/Unlock don't need to be balanced, but one Unlock is needed to
+ // clean/unmap the buffer. Warn if this didn't happen before freeing the
+ // native handle.
+ ALOGW_IF(locked_,
+ "IonBuffer::FreeHandle: freeing a locked handle!!! handle=%p",
+ handle_);
+
+ if (needs_unregister_) {
+ int ret = gralloc_module_->unregisterBuffer(gralloc_module_, handle_);
+ ALOGE_IF(ret < 0,
+ "IonBuffer::FreeHandle: Failed to unregister handle: %s",
+ strerror(-ret));
+
+ native_handle_close(const_cast<native_handle_t*>(handle_));
+ native_handle_delete(const_cast<native_handle_t*>(handle_));
+ } else {
+ int ret = gralloc_device_->free(gralloc_device_, handle_);
+ if (ret < 0) {
+ ALOGE("IonBuffer::FreeHandle: failed to free buffer: %s",
+ strerror(-ret));
+
+ // Not sure if this is the right thing to do. Attempting to prevent a
+ // memory leak of the native handle.
+ native_handle_close(const_cast<native_handle_t*>(handle_));
+ native_handle_delete(const_cast<native_handle_t*>(handle_));
+ }
+ }
+ }
+
+ // Always re-initialize these members, even if handle_ was nullptr, in case
+ // someone was dumb enough to pass a nullptr handle to the constructor or
+ // Reset.
+ handle_ = nullptr;
+ width_ = 0;
+ height_ = 0;
+ layer_count_ = 0;
+ stride_ = 0;
+ layer_stride_ = 0;
+ format_ = 0;
+ usage_ = 0;
+ locked_ = false;
+ needs_unregister_ = false;
+}
+
+int IonBuffer::Alloc(int width, int height, int format, int usage) {
+ ATRACE_NAME("IonBuffer::Alloc");
+ ALOGD_IF(TRACE, "IonBuffer::Alloc: width=%d height=%d format=%d usage=%d",
+ width, height, format, usage);
+
+ int stride;
+ buffer_handle_t handle;
+
+ int ret = gralloc_device_->alloc(gralloc_device_, width, height, format,
+ usage, &handle, &stride);
+ if (ret < 0) {
+ ALOGE("IonBuffer::Alloc: failed to allocate gralloc buffer: %s",
+ strerror(-ret));
+ return ret;
+ }
+
+ Replace(handle, width, height, 1, stride, 0, format, usage, false);
+ return 0;
+}
+
+void IonBuffer::Replace(buffer_handle_t handle, int width, int height,
+ int layer_count, int stride, int layer_stride,
+ int format, int usage, bool needs_unregister) {
+ FreeHandle();
+
+ handle_ = handle;
+ width_ = width;
+ height_ = height;
+ layer_count_ = layer_count;
+ stride_ = stride;
+ layer_stride_ = layer_stride;
+ format_ = format;
+ usage_ = usage;
+ needs_unregister_ = needs_unregister;
+}
+
+void IonBuffer::Reset(buffer_handle_t handle, int width, int height, int stride,
+ int format, int usage) {
+ ALOGD_IF(TRACE,
+ "IonBuffer::Reset: handle=%p width=%d height=%d stride=%d format=%d "
+ "usage=%d",
+ handle, width, height, stride, format, usage);
+
+ Replace(handle, width, height, 1, stride, 0, format, usage, false);
+}
+
+int IonBuffer::Import(buffer_handle_t handle, int width, int height, int stride,
+ int format, int usage) {
+ ATRACE_NAME("IonBuffer::Import1");
+ ALOGD_IF(
+ TRACE,
+ "IonBuffer::Import: handle=%p width=%d height=%d stride=%d format=%d "
+ "usage=%d",
+ handle, width, height, stride, format, usage);
+
+ int ret = gralloc_module_->registerBuffer(gralloc_module_, handle);
+ if (ret < 0) {
+ ALOGE("IonBuffer::Import: failed to import handle: %s", strerror(-ret));
+ return ret;
+ }
+
+ Replace(handle, width, height, 1, stride, 0, format, usage, true);
+ return 0;
+}
+
+int IonBuffer::Import(const int* fd_array, int fd_count, const int* int_array,
+ int int_count, int width, int height, int stride,
+ int format, int usage) {
+ ATRACE_NAME("IonBuffer::Import2");
+ ALOGD_IF(TRACE,
+ "IonBuffer::Import: fd_count=%d int_count=%d width=%d height=%d "
+ "stride=%d format=%d usage=%d",
+ fd_count, int_count, width, height, stride, format, usage);
+
+ if (fd_count < 0 || int_count < 0) {
+ ALOGE("IonBuffer::Import: invalid arguments.");
+ return -EINVAL;
+ }
+
+ native_handle_t* handle = native_handle_create(fd_count, int_count);
+ if (!handle) {
+ ALOGE("IonBuffer::Import: failed to create new native handle.");
+ return -ENOMEM;
+ }
+
+ // Copy fd_array into the first part of handle->data and int_array right
+ // after it.
+ memcpy(handle->data, fd_array, sizeof(int) * fd_count);
+ memcpy(handle->data + fd_count, int_array, sizeof(int) * int_count);
+
+ int ret = Import(handle, width, height, stride, format, usage);
+ if (ret < 0) {
+ ALOGE("IonBuffer::Import: failed to import raw native handle: %s",
+ strerror(-ret));
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+
+ return ret;
+}
+
+int IonBuffer::Duplicate(const IonBuffer* other) {
+ if (!other->handle())
+ return -EINVAL;
+
+ const int fd_count = other->handle()->numFds;
+ const int int_count = other->handle()->numInts;
+
+ if (fd_count < 0 || int_count < 0)
+ return -EINVAL;
+
+ native_handle_t* handle = native_handle_create(fd_count, int_count);
+ if (!handle) {
+ ALOGE("IonBuffer::Duplicate: Failed to create new native handle.");
+ return -ENOMEM;
+ }
+
+ // Duplicate the file descriptors from the other native handle.
+ for (int i = 0; i < fd_count; i++)
+ handle->data[i] = dup(other->handle()->data[i]);
+
+ // Copy the ints after the file descriptors.
+ memcpy(handle->data + fd_count, other->handle()->data + fd_count,
+ sizeof(int) * int_count);
+
+ const int ret = Import(handle, other->width(), other->height(),
+ other->stride(), other->format(), other->usage());
+ if (ret < 0) {
+ ALOGE("IonBuffer::Duplicate: Failed to import duplicate native handle: %s",
+ strerror(-ret));
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+
+ return ret;
+}
+
+int IonBuffer::Lock(int usage, int x, int y, int width, int height,
+ void** address) {
+ ATRACE_NAME("IonBuffer::Lock");
+ ALOGD_IF(TRACE,
+ "IonBuffer::Lock: handle=%p usage=%d x=%d y=%d width=%d height=%d "
+ "address=%p",
+ handle_, usage, x, y, width, height, address);
+
+ // Lock may be called multiple times; but only one Unlock is required.
+ const int err = gralloc_module_->lock(gralloc_module_, handle_, usage, x, y,
+ width, height, address);
+ if (!err)
+ locked_ = true;
+
+ return err;
+}
+
+int IonBuffer::LockYUV(int usage, int x, int y, int width, int height,
+ struct android_ycbcr* yuv) {
+ ATRACE_NAME("IonBuffer::LockYUV");
+ ALOGD_IF(TRACE,
+ "IonBuffer::Lock: handle=%p usage=%d x=%d y=%d width=%d height=%d",
+ handle_, usage, x, y, width, height);
+ const int err = gralloc_module_->lock_ycbcr(gralloc_module_, handle_, usage,
+ x, y, width, height, yuv);
+ if (!err)
+ locked_ = true;
+
+ return err;
+}
+
+int IonBuffer::Unlock() {
+ ATRACE_NAME("IonBuffer::Unlock");
+ ALOGD_IF(TRACE, "IonBuffer::Unlock: handle=%p", handle_);
+
+ // Lock may be called multiple times; but only one Unlock is required.
+ const int err = gralloc_module_->unlock(gralloc_module_, handle_);
+ if (!err)
+ locked_ = false;
+
+ return err;
+}
+
+void IonBuffer::GrallocInit() {
+ static std::once_flag gralloc_flag;
+ std::call_once(gralloc_flag, []() {
+ hw_module_t const* module = nullptr;
+ alloc_device_t* device = nullptr;
+
+ int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
+ ALOGE_IF(err, "IonBuffer::GrallocInit: failed to find the %s module: %s",
+ GRALLOC_HARDWARE_MODULE_ID, strerror(-err));
+
+ err = gralloc_open(module, &device);
+ ALOGE_IF(err, "IonBuffer::GrallocInit: failed to open gralloc device: %s",
+ strerror(-err));
+
+ gralloc_module_ = reinterpret_cast<gralloc_module_t const*>(module);
+ gralloc_device_ = device;
+ });
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhub/mocks/client/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/mocks/client/private/dvr/buffer_hub_client.h
new file mode 100644
index 0000000..33816fa
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/client/private/dvr/buffer_hub_client.h
@@ -0,0 +1,57 @@
+#ifndef LIB_LIBBUFFERHUB_PRIVATE_DVR_BUFFER_HUB_CLIENT_H_ // NOLINT
+#define LIB_LIBBUFFERHUB_PRIVATE_DVR_BUFFER_HUB_CLIENT_H_
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+// TODO(jwcai) mock not need for now
+class native_handle_t;
+
+namespace android {
+namespace dvr {
+
+// TODO(jwcai) mock not need for now
+class IonBuffer;
+
+class BufferHubBuffer {
+ public:
+ MOCK_METHOD1(Poll, int(int timeout_ms));
+ MOCK_METHOD6(Lock, bool(int usage, int x, int y, int width, int height,
+ void** addr));
+ MOCK_METHOD0(Unlock, int());
+
+ MOCK_METHOD0(native_handle, native_handle_t*());
+ MOCK_METHOD0(buffer, IonBuffer*());
+ MOCK_METHOD0(event_fd, int());
+
+ MOCK_METHOD0(id, int());
+ MOCK_METHOD0(width, int());
+ MOCK_METHOD0(height, int());
+ MOCK_METHOD0(stride, int());
+ MOCK_METHOD0(format, int());
+ MOCK_METHOD0(usage, int());
+};
+
+class BufferProducer : public BufferHubBuffer {
+ public:
+ // Note that static method |CreateBuffer| and |Import| are not mocked
+ // here, they are just implementation details and thus not needed.
+ MOCK_METHOD2(Post, int(int ready_fence, uint64_t sequence));
+ MOCK_METHOD1(Gain, int(int* release_fence));
+
+ static BufferProducer* staticObject;
+};
+
+class BufferConsumer : public BufferHubBuffer {
+ public:
+ MOCK_METHOD2(Acquire, int(int* ready_fence, uint64_t* sequence));
+ MOCK_METHOD1(Release, int(int release_fence));
+ MOCK_METHOD0(Discard, int());
+ MOCK_METHOD3(DoAcquire,
+ int(int* ready_fence, void* meta, size_t meta_size_bytes));
+
+ static BufferConsumer* staticObject;
+};
+
+} // namespace dvr
+} // namespace android
+#endif // LIB_LIBBUFFERHUB_PRIVATE_DVR_BUFFER_HUB_CLIENT_H_ //NOLINT
diff --git a/libs/vr/libbufferhub/mocks/gralloc/BUILD.gn b/libs/vr/libbufferhub/mocks/gralloc/BUILD.gn
new file mode 100644
index 0000000..9674c7c
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/gralloc/BUILD.gn
@@ -0,0 +1,23 @@
+config("gralloc_config") {
+ include_dirs = [ "." ]
+}
+
+static_library("gralloc") {
+ testonly = true
+
+ sources = [
+ "gralloc.cpp",
+ "gralloc.h",
+ ]
+
+ include_dirs = [
+ "//system/core/include",
+ "//hardware/libhardware/include",
+ ]
+
+ public_deps = [
+ "//dreamos/external/gmock",
+ ]
+
+ public_configs = [ ":gralloc_config" ]
+}
diff --git a/libs/vr/libbufferhub/mocks/gralloc/gralloc.cpp b/libs/vr/libbufferhub/mocks/gralloc/gralloc.cpp
new file mode 100644
index 0000000..4a923ec
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/gralloc/gralloc.cpp
@@ -0,0 +1,68 @@
+#include <gralloc_mock.h>
+#include <hardware/gralloc.h>
+
+static alloc_device_t sdevice;
+
+static int local_registerBuffer(struct gralloc_module_t const*,
+ buffer_handle_t handle) {
+ return GrallocMock::staticObject->registerBuffer(handle);
+}
+
+static int local_unregisterBuffer(struct gralloc_module_t const*,
+ buffer_handle_t handle) {
+ return GrallocMock::staticObject->unregisterBuffer(handle);
+}
+
+static int local_unlock(struct gralloc_module_t const*,
+ buffer_handle_t handle) {
+ return GrallocMock::staticObject->unlock(handle);
+}
+
+static int local_lock(struct gralloc_module_t const*, buffer_handle_t handle,
+ int usage, int l, int t, int w, int h, void** vaddr) {
+ return GrallocMock::staticObject->lock(handle, usage, l, t, w, h, vaddr);
+}
+
+static int local_alloc(struct alloc_device_t*, int w, int h, int format,
+ int usage, buffer_handle_t* handle, int* stride) {
+ return GrallocMock::staticObject->alloc(w, h, format, usage, handle, stride);
+}
+
+static int local_free(struct alloc_device_t*, buffer_handle_t handle) {
+ return GrallocMock::staticObject->free(handle);
+}
+
+static int local_open(const struct hw_module_t*, const char*,
+ struct hw_device_t** device) {
+ sdevice.alloc = local_alloc;
+ sdevice.free = local_free;
+ *device = reinterpret_cast<hw_device_t*>(&sdevice);
+ return 0;
+}
+
+static hw_module_methods_t smethods;
+
+static gralloc_module_t smodule;
+
+int hw_get_module(const char*, const struct hw_module_t** module) {
+ smodule.registerBuffer = local_registerBuffer;
+ smodule.unregisterBuffer = local_unregisterBuffer;
+ smodule.lock = local_lock;
+ smodule.unlock = local_unlock;
+ smethods.open = local_open;
+ smodule.common.methods = &smethods;
+ *module = reinterpret_cast<hw_module_t*>(&smodule);
+ return 0;
+}
+
+int native_handle_close(const native_handle_t* handle) {
+ return GrallocMock::staticObject->native_handle_close(handle);
+}
+
+int native_handle_delete(native_handle_t* handle) {
+ return GrallocMock::staticObject->native_handle_delete(handle);
+}
+
+native_handle_t* native_handle_create(int numFds, int numInts) {
+ return GrallocMock::staticObject->native_handle_create(numFds, numInts);
+}
diff --git a/libs/vr/libbufferhub/mocks/gralloc/gralloc_mock.h b/libs/vr/libbufferhub/mocks/gralloc/gralloc_mock.h
new file mode 100644
index 0000000..f62f579
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/gralloc/gralloc_mock.h
@@ -0,0 +1,23 @@
+#ifndef LIB_LIBBUFFERHUB_MOCKS_GRALLOC_GRALLOC_MOCK_H_
+#define LIB_LIBBUFFERHUB_MOCKS_GRALLOC_GRALLOC_MOCK_H_
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hardware/gralloc.h>
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class GrallocMock {
+ public:
+ // Add methods here.
+ MOCK_METHOD1(native_handle_close, int(const native_handle_t*));
+ MOCK_METHOD1(native_handle_delete, int(native_handle_t*));
+ MOCK_METHOD2(native_handle_create, native_handle_t*(int, int));
+ MOCK_METHOD1(registerBuffer, int(buffer_handle_t));
+ MOCK_METHOD1(unregisterBuffer, int(buffer_handle_t));
+ MOCK_METHOD7(lock, int(buffer_handle_t, int, int, int, int, int, void**));
+ MOCK_METHOD1(unlock, int(buffer_handle_t));
+ MOCK_METHOD6(alloc, int(int, int, int, int, buffer_handle_t*, int*));
+ MOCK_METHOD1(free, int(buffer_handle_t));
+ static GrallocMock* staticObject;
+};
+
+#endif // LIB_LIBBUFFERHUB_MOCKS_GRALLOC_GRALLOC_MOCK_H_
diff --git a/libs/vr/libbufferhub/mocks/ion_buffer/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/mocks/ion_buffer/private/dvr/ion_buffer.h
new file mode 100644
index 0000000..fac6db0
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/ion_buffer/private/dvr/ion_buffer.h
@@ -0,0 +1,78 @@
+// This file has a big hack, it "mocks" the actual IonBuffer by redefining
+// it with mock methods and using the same header guard to prevent the original
+// definition from being included in the same context.
+#ifndef LIB_LIBBUFFERHUB_PRIVATE_DVR_ION_BUFFER_H_ // NOLINT
+#define LIB_LIBBUFFERHUB_PRIVATE_DVR_ION_BUFFER_H_
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <hardware/gralloc.h>
+
+namespace android {
+namespace dvr {
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class IonBufferMock {
+ public:
+ IonBufferMock() {}
+ MOCK_METHOD0(GetGrallocModuleImpl, gralloc_module_t const*());
+ MOCK_METHOD6(Import, int(buffer_handle_t handle, int width, int height,
+ int stride, int format, int usage));
+ MOCK_METHOD9(Import, int(const int* fd_array, int fd_count,
+ const int* int_array, int int_count, int width,
+ int height, int stride, int format, int usage));
+ MOCK_METHOD6(Lock, int(int usage, int x, int y, int width, int height,
+ void** address));
+ MOCK_METHOD0(Unlock, int());
+ MOCK_CONST_METHOD0(handle, buffer_handle_t());
+ MOCK_CONST_METHOD0(width, int());
+ MOCK_CONST_METHOD0(height, int());
+ MOCK_CONST_METHOD0(layer_count, int());
+ MOCK_CONST_METHOD0(stride, int());
+ MOCK_CONST_METHOD0(layer_stride, int());
+ MOCK_CONST_METHOD0(format, int());
+ MOCK_CONST_METHOD0(usage, int());
+};
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class IonBuffer {
+ public:
+ IonBuffer() : mock_(new IonBufferMock) {
+ if (initializer) {
+ initializer(mock_.get());
+ }
+ }
+ IonBuffer(IonBuffer&& other) = default;
+ static gralloc_module_t const* GetGrallocModule() {
+ return staticObject->GetGrallocModuleImpl();
+ }
+ int Import(buffer_handle_t handle, int width, int height, int stride,
+ int format, int usage) {
+ return mock_->Import(handle, width, height, stride, format, usage);
+ }
+ int Import(const int* fd_array, int fd_count, const int* int_array,
+ int int_count, int width, int height, int stride, int format,
+ int usage) {
+ return mock_->Import(fd_array, fd_count, int_array, int_count, width,
+ height, stride, format, usage);
+ }
+ int Lock(int usage, int x, int y, int width, int height, void** address) {
+ return mock_->Lock(usage, x, y, width, height, address);
+ }
+ int Unlock() { return mock_->Unlock(); }
+ buffer_handle_t handle() const { return mock_->handle(); }
+ int width() const { return mock_->width(); }
+ int height() const { return mock_->height(); }
+ int layer_count() const { return mock_->layer_count(); }
+ int stride() const { return mock_->stride(); }
+ int layer_stride() const { return mock_->layer_stride(); }
+ int format() const { return mock_->format(); }
+ int usage() const { return mock_->usage(); }
+ std::unique_ptr<IonBufferMock> mock_;
+ static IonBufferMock* staticObject;
+ static void (*initializer)(IonBufferMock* target);
+};
+
+} // namespace dvr
+} // namespace android
+#endif // LIB_LIBBUFFERHUB_PRIVATE_DVR_ION_BUFFER_H_ - NOLINT
diff --git a/libs/vr/libbufferhub/tests/Android.mk b/libs/vr/libbufferhub/tests/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/libs/vr/libbufferhub/tests/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/libs/vr/libbufferhub/tests/ion_buffer/Android.mk b/libs/vr/libbufferhub/tests/ion_buffer/Android.mk
new file mode 100644
index 0000000..3bfdb7b
--- /dev/null
+++ b/libs/vr/libbufferhub/tests/ion_buffer/Android.mk
@@ -0,0 +1,74 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+COMPONENT_TOP := ${LOCAL_PATH}/../..
+
+LOCAL_SRC_FILES := \
+ ion_buffer-test.cpp \
+ ../../ion_buffer.cpp \
+ ../../mocks/gralloc/gralloc.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libc \
+ libcutils \
+ libutils \
+ liblog
+
+LOCAL_STATIC_LIBRARIES := \
+ libgmock
+
+LOCAL_C_INCLUDES := \
+ ${COMPONENT_TOP}/mocks/gralloc \
+ ${COMPONENT_TOP}/include \
+ $(TOP)/system/core/base/include
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := ${LOCAL_C_INCLUDES}
+
+LOCAL_NATIVE_COVERAGE := true
+
+LOCAL_CFLAGS := -DTRACE=0 -g
+
+LOCAL_MODULE := ion_buffer-test
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ ion_buffer-test.cpp \
+ ../../ion_buffer.cpp \
+ ../../mocks/gralloc/gralloc.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog
+
+LOCAL_STATIC_LIBRARIES := \
+ libgmock_host
+
+LOCAL_C_INCLUDES := \
+ ${COMPONENT_TOP}/mocks/gralloc \
+ ${COMPONENT_TOP}/include \
+ $(TOP)/system/core/base/include
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := ${LOCAL_C_INCLUDES}
+
+LOCAL_NATIVE_COVERAGE := true
+
+LOCAL_CFLAGS := -DTRACE=0
+
+LOCAL_MODULE := ion_buffer-host_test
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_HOST_NATIVE_TEST)
+
+.PHONY: dvr_host_native_unit_tests
+dvr_host_native_unit_tests: ion_buffer-host_test
+ifeq (true,$(NATIVE_COVERAGE))
+ ion_buffer-host_test: llvm-cov
+ ion_buffer-test: llvm-cov
+ # This shouldn't be necessary, but the default build with
+ # NATIVE_COVERAGE=true manages to ion_buffer-test without
+ # building llvm-cov (droid is the default target).
+ droid: llvm-cov
+endif
diff --git a/libs/vr/libbufferhub/tests/ion_buffer/ion_buffer-test.cpp b/libs/vr/libbufferhub/tests/ion_buffer/ion_buffer-test.cpp
new file mode 100644
index 0000000..68f82d7
--- /dev/null
+++ b/libs/vr/libbufferhub/tests/ion_buffer/ion_buffer-test.cpp
@@ -0,0 +1,375 @@
+#include <gmock/gmock.h>
+#include <gralloc_mock.h>
+#include <gtest/gtest.h>
+#include <private/dvr/ion_buffer.h>
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using android::dvr::IonBuffer;
+
+GrallocMock* GrallocMock::staticObject = nullptr;
+
+namespace {
+
+const int w1 = 100;
+const int h1 = 200;
+const int d1 = 2;
+const int f1 = 1;
+const int u1 = 3;
+const int stride1 = 8;
+const int layer_stride1 = 8;
+native_handle_t handle1;
+const int w2 = 150;
+const int h2 = 300;
+const int d2 = 4;
+const int f2 = 2;
+const int u2 = 5;
+const int stride2 = 4;
+const int layer_stride2 = 4;
+native_handle_t handle2;
+const int kMaxFd = 10;
+const int kMaxInt = 10;
+char handleData[sizeof(native_handle_t) + (kMaxFd + kMaxInt) * sizeof(int)];
+native_handle_t* const dataHandle =
+ reinterpret_cast<native_handle_t*>(handleData);
+char refData[sizeof(native_handle_t) + (kMaxFd + kMaxInt) * sizeof(int)];
+native_handle_t* const refHandle = reinterpret_cast<native_handle_t*>(refData);
+
+class IonBufferUnitTest : public ::testing::Test {
+ protected:
+ // You can remove any or all of the following functions if its body
+ // is empty.
+
+ IonBufferUnitTest() {
+ GrallocMock::staticObject = new GrallocMock;
+ // You can do set-up work for each test here.
+ // most ServicefsClients will use this initializer. Use as the
+ // default.
+ }
+
+ virtual ~IonBufferUnitTest() {
+ delete GrallocMock::staticObject;
+ GrallocMock::staticObject = nullptr;
+ // You can do clean-up work that doesn't throw exceptions here.
+ }
+
+ // If the constructor and destructor are not enough for setting up
+ // and cleaning up each test, you can define the following methods:
+
+ void SetUp() override {
+ // Code here will be called immediately after the constructor (right
+ // before each test).
+ }
+
+ void TearDown() override {
+ // Code here will be called immediately after each test (right
+ // before the destructor).
+ }
+};
+
+void TestIonBufferState(const IonBuffer& buffer, int w, int h, int d, int f,
+ int u, native_handle_t* handle, int stride,
+ int layer_stride) {
+ EXPECT_EQ(buffer.width(), w);
+ EXPECT_EQ(buffer.height(), h);
+ EXPECT_EQ(buffer.layer_count(), d);
+ EXPECT_EQ(buffer.format(), f);
+ EXPECT_EQ(buffer.usage(), u);
+ EXPECT_EQ(buffer.handle(), handle);
+ EXPECT_EQ(buffer.stride(), stride);
+ EXPECT_EQ(buffer.layer_stride(), layer_stride);
+}
+
+TEST_F(IonBufferUnitTest, TestFreeOnDestruction) {
+ // Set up |alloc|(|w1...|) to succeed once and fail on others calls.
+ EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+ .Times(1)
+ .WillOnce(DoAll(SetArgPointee<4>(&handle1), SetArgPointee<5>(stride1),
+ Return(0)));
+ // Set up |free| to be called once.
+ EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+
+ IonBuffer buffer;
+ // First call to |alloc| with |w1...| set up to succeed.
+ int ret = buffer.Alloc(w1, h1, f1, u1);
+ EXPECT_EQ(ret, 0);
+ TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+
+ // Scoped destructor will be called, calling |free| on |handle1|.
+}
+
+TEST_F(IonBufferUnitTest, TestAlloc) {
+ IonBuffer buffer;
+ // Set up |alloc|(|w1...|) to succeed once and fail on others calls.
+ EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+ .Times(2)
+ .WillOnce(DoAll(SetArgPointee<4>(&handle1), SetArgPointee<5>(stride1),
+ Return(0)))
+ .WillRepeatedly(Return(-1));
+
+ // Set up |alloc|(|w2...|) to succeed once and fail on others calls.
+ EXPECT_CALL(*GrallocMock::staticObject, alloc(w2, h2, f2, u2, _, _))
+ .Times(2)
+ .WillOnce(DoAll(SetArgPointee<4>(&handle2), SetArgPointee<5>(stride2),
+ Return(0)))
+ .WillRepeatedly(Return(-1));
+ EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*GrallocMock::staticObject, free(&handle2))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+
+ // First call to |alloc| with |w1...| set up to succeed.
+ int ret = buffer.Alloc(w1, h1, f1, u1);
+ EXPECT_EQ(ret, 0);
+ TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+
+ // First call to |alloc| with |w2...| set up to succeed, |free| should be
+ // called once on |handle1|.
+ ret = buffer.Alloc(w2, h2, f2, u2);
+ EXPECT_EQ(ret, 0);
+ TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+
+ // Second call to |alloc| with |w1| is set up to fail.
+ ret = buffer.Alloc(w1, h1, f1, u1);
+ EXPECT_LT(ret, 0);
+ TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+
+ // |free| on |handle2| should be called here.
+ buffer.FreeHandle();
+ TestIonBufferState(buffer, 0, 0, 0, 0, 0, nullptr, 0, 0);
+
+ // |alloc| is set up to fail.
+ ret = buffer.Alloc(w2, h2, f2, u2);
+ EXPECT_LT(ret, 0);
+ TestIonBufferState(buffer, 0, 0, 0, 0, 0, nullptr, 0, 0);
+}
+
+TEST_F(IonBufferUnitTest, TestReset) {
+ IonBuffer buffer;
+ EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*GrallocMock::staticObject, free(&handle2))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+ buffer.Reset(&handle1, w1, h1, stride1, f1, u1);
+ TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+ buffer.Reset(&handle2, w2, h2, stride2, f2, u2);
+ TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+ buffer.FreeHandle();
+}
+
+TEST_F(IonBufferUnitTest, TestImport1) {
+ IonBuffer buffer;
+ EXPECT_CALL(*GrallocMock::staticObject, registerBuffer(&handle1))
+ .Times(3)
+ .WillOnce(Return(0))
+ .WillRepeatedly(Return(-1));
+ EXPECT_CALL(*GrallocMock::staticObject, registerBuffer(&handle2))
+ .Times(3)
+ .WillOnce(Return(0))
+ .WillOnce(Return(-1))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*GrallocMock::staticObject, unregisterBuffer(&handle1))
+ .Times(1)
+ .WillOnce(Return(0));
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_close(&handle1))
+ .Times(1);
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_delete(&handle1))
+ .Times(1);
+ EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+ .Times(1)
+ .WillRepeatedly(DoAll(SetArgPointee<4>(&handle1),
+ SetArgPointee<5>(stride1), Return(0)));
+ EXPECT_CALL(*GrallocMock::staticObject, unregisterBuffer(&handle2))
+ .Times(2)
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_close(&handle2))
+ .Times(2);
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_delete(&handle2))
+ .Times(2);
+ EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+
+ int ret = buffer.Import(&handle1, w1, h1, stride1, f1, u1);
+ EXPECT_EQ(ret, 0);
+ TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+ ret = buffer.Import(&handle2, w2, h2, stride2, f2, u2);
+ EXPECT_EQ(ret, 0);
+ TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+ ret = buffer.Alloc(w1, h1, f1, u1);
+ EXPECT_EQ(ret, 0);
+ ret = buffer.Import(&handle2, w2, h2, stride2, f2, u2);
+ EXPECT_LT(ret, 0);
+ TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+ ret = buffer.Import(&handle2, w2, h2, stride2, f2, u2);
+ EXPECT_EQ(ret, 0);
+ TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+ ret = buffer.Import(&handle1, w1, h1, stride1, f1, u1);
+ EXPECT_LT(ret, 0);
+ TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+ buffer.FreeHandle();
+ ret = buffer.Import(&handle1, w1, h1, stride1, f1, u1);
+ EXPECT_LT(ret, 0);
+ TestIonBufferState(buffer, 0, 0, 0, 0, 0, nullptr, 0, 0);
+}
+
+native_handle_t* native_handle_create_impl(int nFds, int nInts) {
+ if ((nFds + nInts) > (kMaxFd + kMaxInt))
+ return nullptr;
+ dataHandle->version = sizeof(native_handle_t);
+ dataHandle->numFds = nFds;
+ dataHandle->numInts = nInts;
+ for (int i = 0; i < nFds + nInts; i++)
+ dataHandle->data[i] = 0;
+ return dataHandle;
+}
+
+TEST_F(IonBufferUnitTest, TestImport2) {
+ IonBuffer buffer;
+ int ints[] = {211, 313, 444};
+ int fds[] = {-1, -1};
+ int ni = sizeof(ints) / sizeof(ints[0]);
+ int nfd = sizeof(fds) / sizeof(fds[0]);
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_create(nfd, ni))
+ .Times(3)
+ .WillOnce(Return(nullptr))
+ .WillRepeatedly(Invoke(native_handle_create_impl));
+ EXPECT_CALL(*GrallocMock::staticObject, registerBuffer(dataHandle))
+ .Times(2)
+ .WillOnce(Return(-1))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_close(dataHandle))
+ .Times(2);
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_delete(dataHandle))
+ .Times(2);
+ EXPECT_CALL(*GrallocMock::staticObject, unregisterBuffer(dataHandle))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+
+ int ret = buffer.Import(fds, -1, ints, ni, w1, h1, stride1, f1, u1);
+ EXPECT_LT(ret, 0);
+ ret = buffer.Import(fds, nfd, ints, -1, w1, h1, stride1, f1, u1);
+ EXPECT_LT(ret, 0);
+ ret = buffer.Import(fds, nfd, ints, ni, w1, h1, stride1, f1, u1);
+ EXPECT_LT(ret, 0);
+ ret = buffer.Import(fds, nfd, ints, ni, w1, h1, stride1, f1, u1);
+ EXPECT_LT(ret, 0);
+ ret = buffer.Import(fds, nfd, ints, ni, w1, h1, stride1, f1, u1);
+ EXPECT_EQ(ret, 0);
+ TestIonBufferState(buffer, w1, h1, 1, f1, u1, dataHandle, stride1, 0);
+ EXPECT_EQ(dataHandle->numFds, nfd);
+ EXPECT_EQ(dataHandle->numInts, ni);
+ for (int i = 0; i < nfd; i++)
+ EXPECT_EQ(dataHandle->data[i], fds[i]);
+ for (int i = 0; i < ni; i++)
+ EXPECT_EQ(dataHandle->data[nfd + i], ints[i]);
+ buffer.FreeHandle();
+}
+
+TEST_F(IonBufferUnitTest, TestDuplicate) {
+ IonBuffer buffer;
+ IonBuffer ref;
+ int ints[] = {211, 313, 444};
+ int fds[] = {-1, -1};
+ int ni = sizeof(ints) / sizeof(ints[0]);
+ int nfd = sizeof(fds) / sizeof(fds[0]);
+
+ for (int i = 0; i < nfd; i++) {
+ refHandle->data[i] = fds[i];
+ }
+ for (int i = 0; i < ni; i++) {
+ refHandle->data[i + nfd] = ints[i];
+ }
+
+ EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+ .Times(1)
+ .WillRepeatedly(DoAll(SetArgPointee<4>(refHandle),
+ SetArgPointee<5>(stride1), Return(0)));
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_create(nfd, ni))
+ .Times(3)
+ .WillOnce(Return(nullptr))
+ .WillRepeatedly(Invoke(native_handle_create_impl));
+ EXPECT_CALL(*GrallocMock::staticObject, registerBuffer(dataHandle))
+ .Times(2)
+ .WillOnce(Return(-1))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_close(dataHandle))
+ .Times(2);
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_delete(dataHandle))
+ .Times(2);
+ EXPECT_CALL(*GrallocMock::staticObject, unregisterBuffer(dataHandle))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*GrallocMock::staticObject, free(refHandle))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+
+ int ret = buffer.Duplicate(&ref);
+ EXPECT_LT(ret, 0);
+ ret = ref.Alloc(w1, h1, f1, u1);
+ EXPECT_EQ(ret, 0);
+ refHandle->numFds = -1;
+ refHandle->numInts = 0;
+ ret = buffer.Duplicate(&ref);
+ EXPECT_LT(ret, 0);
+ refHandle->numFds = nfd;
+ refHandle->numInts = ni;
+ ret = buffer.Duplicate(&ref);
+ EXPECT_LT(ret, 0);
+ ret = buffer.Duplicate(&ref);
+ EXPECT_LT(ret, 0);
+ ret = buffer.Duplicate(&ref);
+ EXPECT_EQ(ret, 0);
+ TestIonBufferState(buffer, w1, h1, 1, f1, u1, dataHandle, stride1, 0);
+ EXPECT_EQ(dataHandle->numFds, nfd);
+ EXPECT_EQ(dataHandle->numInts, ni);
+ for (int i = 0; i < nfd; i++)
+ EXPECT_LT(dataHandle->data[i], 0);
+ for (int i = 0; i < ni; i++)
+ EXPECT_EQ(dataHandle->data[nfd + i], ints[i]);
+ buffer.FreeHandle();
+ ref.FreeHandle();
+}
+
+TEST_F(IonBufferUnitTest, TestLockUnlock) {
+ IonBuffer buffer;
+ const int x = 12;
+ const int y = 24;
+ const int value1 = 17;
+ const int value2 = 25;
+ void* addr1;
+ void** addr = &addr1;
+
+ EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+ .Times(1)
+ .WillRepeatedly(DoAll(SetArgPointee<4>(&handle1),
+ SetArgPointee<5>(stride1), Return(0)));
+ EXPECT_CALL(*GrallocMock::staticObject,
+ lock(&handle1, u2, x, y, w2, h2, addr))
+ .Times(1)
+ .WillRepeatedly(Return(value1));
+ EXPECT_CALL(*GrallocMock::staticObject, unlock(&handle1))
+ .Times(1)
+ .WillRepeatedly(Return(value2));
+ EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+
+ int ret = buffer.Alloc(w1, h1, f1, u1);
+ EXPECT_EQ(ret, 0);
+ ret = buffer.Lock(u2, x, y, w2, h2, addr);
+ EXPECT_EQ(ret, value1);
+ ret = buffer.Unlock();
+ EXPECT_EQ(ret, value2);
+ buffer.FreeHandle();
+}
+
+} // namespace