Merge "media.bufferpool@1.0 implementation"
diff --git a/media/libstagefright/codec2/Android.bp b/media/libstagefright/codec2/Android.bp
index e1ac44e..1c32953 100644
--- a/media/libstagefright/codec2/Android.bp
+++ b/media/libstagefright/codec2/Android.bp
@@ -1,6 +1,9 @@
cc_library_shared {
name: "libstagefright_codec2",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
tags: [
"optional",
diff --git a/media/libstagefright/codec2/vndk/Android.bp b/media/libstagefright/codec2/vndk/Android.bp
index d6cbe96..d7709a0 100644
--- a/media/libstagefright/codec2/vndk/Android.bp
+++ b/media/libstagefright/codec2/vndk/Android.bp
@@ -54,3 +54,7 @@
"-std=c++14",
],
}
+
+subdirs = [
+ "bufferpool",
+]
diff --git a/media/libstagefright/codec2/vndk/bufferpool/Accessor.cpp b/media/libstagefright/codec2/vndk/bufferpool/Accessor.cpp
new file mode 100644
index 0000000..3571ed5
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/Accessor.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "Accessor.h"
+#include "AccessorImpl.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::bufferpool::V1_0::IAccessor follow.
+Return<void> Accessor::connect(connect_cb _hidl_cb) {
+ sp<Connection> connection;
+ ConnectionId connectionId;
+ const QueueDescriptor* fmqDesc;
+
+ ResultStatus status = connect(&connection, &connectionId, &fmqDesc);
+ if (status == ResultStatus::OK) {
+ _hidl_cb(status, connection, connectionId, *fmqDesc);
+ } else {
+ _hidl_cb(status, nullptr, -1LL,
+ android::hardware::MQDescriptorSync<BufferStatusMessage>(
+ std::vector<android::hardware::GrantorDescriptor>(),
+ nullptr /* nhandle */, 0 /* size */));
+ }
+ return Void();
+}
+
+Accessor::Accessor(const std::shared_ptr<C2Allocator> &allocator, bool linear)
+ : mImpl(new Impl(allocator, linear)) {}
+
+Accessor::~Accessor() {
+}
+
+bool Accessor::isValid() {
+ return (bool)mImpl;
+}
+
+ResultStatus Accessor::allocate(
+ ConnectionId connectionId,
+ const std::vector<uint8_t> ¶ms,
+ BufferId *bufferId, const native_handle_t** handle) {
+ if (mImpl) {
+ return mImpl->allocate(connectionId, params, bufferId, handle);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::fetch(
+ ConnectionId connectionId, TransactionId transactionId,
+ BufferId bufferId, const native_handle_t** handle) {
+ if (mImpl) {
+ return mImpl->fetch(connectionId, transactionId, bufferId, handle);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::connect(
+ sp<Connection> *connection, ConnectionId *pConnectionId,
+ const QueueDescriptor** fmqDescPtr) {
+ if (mImpl) {
+ return mImpl->connect(this, connection, pConnectionId, fmqDescPtr);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::close(ConnectionId connectionId) {
+ if (mImpl) {
+ return mImpl->close(connectionId);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+//IAccessor* HIDL_FETCH_IAccessor(const char* /* name */) {
+// return new Accessor();
+//}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/codec2/vndk/bufferpool/Accessor.h b/media/libstagefright/codec2/vndk/bufferpool/Accessor.h
new file mode 100644
index 0000000..6fd82ba
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/Accessor.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
+
+#include <android/hardware/media/bufferpool/1.0/IAccessor.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <C2Buffer.h>
+#include <BufferPoolTypes.h>
+#include "BufferStatus.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct Connection;
+
+/**
+ * A buffer pool accessor which enables a buffer pool to communicate with buffer
+ * pool clients. 1:1 correspondense holds between a buffer pool and an accessor.
+ */
+struct Accessor : public IAccessor {
+ // Methods from ::android::hardware::media::bufferpool::V1_0::IAccessor follow.
+ Return<void> connect(connect_cb _hidl_cb) override;
+
+ /**
+ * Creates a buffer pool accessor which uses the specified allocator.
+ *
+ * @param allocator buffer allocator.
+ * @param linear whether the allocator is linear or not.
+ */
+ Accessor(const std::shared_ptr<C2Allocator> &allocator, bool linear);
+
+ /** Destructs a buffer pool accessor. */
+ ~Accessor();
+
+ /** Returns whether the accessor is valid. */
+ bool isValid();
+
+ /** Allocates a buffer form a buffer pool.
+ *
+ * @param connectionId the connection id of the client.
+ * @param params the allocation parameters.
+ * @param bufferId the id of the allocated buffer.
+ * @param handle the native handle of the allocated buffer.
+ *
+ * @return OK when a buffer is successfully allocated.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus allocate(
+ ConnectionId connectionId,
+ const std::vector<uint8_t>& params,
+ BufferId *bufferId,
+ const native_handle_t** handle);
+
+ /**
+ * Fetches a buffer for the specified transaction.
+ *
+ * @param connectionId the id of receiving connection(client).
+ * @param transactionId the id of the transfer transaction.
+ * @param bufferId the id of the buffer to be fetched.
+ * @param handle the native handle of the fetched buffer.
+ *
+ * @return OK when a buffer is successfully fetched.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus fetch(
+ ConnectionId connectionId,
+ TransactionId transactionId,
+ BufferId bufferId,
+ const native_handle_t** handle);
+
+ /**
+ * Makes a connection to the buffer pool. The buffer pool client uses the
+ * created connection in order to communicate with the buffer pool. An
+ * FMQ for buffer status message is also created for the client.
+ *
+ * @param connection created connection
+ * @param pConnectionId the id of the created connection
+ * @param fmqDescPtr FMQ descriptor for shared buffer status message
+ * queue between a buffer pool and the client.
+ *
+ * @return OK when a connection is successfully made.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus connect(
+ sp<Connection> *connection, ConnectionId *pConnectionId,
+ const QueueDescriptor** fmqDescPtr);
+
+ /**
+ * Closes the specified connection to the client.
+ *
+ * @param connectionId the id of the connection.
+ *
+ * @return OK when the connection is closed.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus close(ConnectionId connectionId);
+
+private:
+ class Impl;
+ std::unique_ptr<Impl> mImpl;
+};
+
+// FIXME: most likely delete, this is only for passthrough implementations
+// extern "C" IAccessor* HIDL_FETCH_IAccessor(const char* name);
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
diff --git a/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.cpp b/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.cpp
new file mode 100644
index 0000000..f8aec53
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.cpp
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define LOG_TAG "bufferpool"
+//#define LOG_NDEBUG 0
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <utils/Log.h>
+#include <C2Buffer.h>
+#include "AccessorImpl.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+// Buffer structure in bufferpool process
+struct InternalBuffer {
+ BufferId mId;
+ size_t mOwnerCount;
+ size_t mTransactionCount;
+ const bool mLinear;
+ const std::shared_ptr<C2LinearAllocation> mLinearAllocation;
+ const std::shared_ptr<C2GraphicAllocation> mGraphicAllocation;
+ const std::vector<uint8_t> mConfig;
+
+ InternalBuffer(
+ BufferId id,
+ const std::shared_ptr<C2LinearAllocation> &alloc,
+ const std::vector<uint8_t> &allocConfig)
+ : mId(id), mOwnerCount(0), mTransactionCount(0),
+ mLinear(true), mLinearAllocation(alloc),
+ mGraphicAllocation(nullptr),
+ mConfig(allocConfig) {}
+
+ InternalBuffer(
+ BufferId id,
+ const std::shared_ptr<C2GraphicAllocation> &alloc,
+ const std::vector<uint8_t> &allocConfig)
+ : mId(id), mOwnerCount(0), mTransactionCount(0),
+ mLinear(false), mLinearAllocation(nullptr),
+ mGraphicAllocation(alloc),
+ mConfig(allocConfig) {}
+
+ const native_handle_t *handle() {
+ if (mLinear) {
+ return mLinearAllocation->handle();
+ } else {
+ return mGraphicAllocation->handle();
+ }
+ }
+
+ // TODO : support non exact matching. e.g) capacity
+ bool isRecyclable(const std::vector<uint8_t> &config) {
+ if (mConfig.size() == config.size()) {
+ for (size_t i = 0; i < config.size(); ++i) {
+ if (mConfig[i] != config[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+};
+
+struct TransactionStatus {
+ TransactionId mId;
+ BufferId mBufferId;
+ ConnectionId mSender;
+ ConnectionId mReceiver;
+ BufferStatus mStatus;
+ int64_t mTimestampUs;
+ bool mSenderValidated;
+
+ TransactionStatus(const BufferStatusMessage &message, int64_t timestampUs) {
+ mId = message.transactionId;
+ mBufferId = message.bufferId;
+ mStatus = message.newStatus;
+ mTimestampUs = timestampUs;
+ if (mStatus == BufferStatus::TRANSFER_TO) {
+ mSender = message.connectionId;
+ mReceiver = message.targetConnectionId;
+ mSenderValidated = true;
+ } else {
+ mSender = -1LL;
+ mReceiver = message.connectionId;
+ mSenderValidated = false;
+ }
+ }
+};
+
+// Helper template methods for handling map of set.
+template<class T, class U>
+bool insert(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+ auto iter = mapOfSet->find(key);
+ if (iter == mapOfSet->end()) {
+ std::set<U> valueSet{value};
+ mapOfSet->insert(std::make_pair(key, valueSet));
+ return true;
+ } else if (iter->second.find(value) == iter->second.end()) {
+ iter->second.insert(value);
+ return true;
+ }
+ return false;
+}
+
+template<class T, class U>
+bool erase(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+ bool ret = false;
+ auto iter = mapOfSet->find(key);
+ if (iter != mapOfSet->end()) {
+ if (iter->second.erase(value) > 0) {
+ ret = true;
+ }
+ if (iter->second.size() == 0) {
+ mapOfSet->erase(iter);
+ }
+ }
+ return ret;
+}
+
+template<class T, class U>
+bool contains(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+ auto iter = mapOfSet->find(key);
+ if (iter != mapOfSet->end()) {
+ auto setIter = iter->second.find(value);
+ return setIter != iter->second.end();
+ }
+ return false;
+}
+
+
+int32_t Accessor::Impl::sPid = getpid();
+uint32_t Accessor::Impl::sSeqId = time(NULL);
+
+Accessor::Impl::Impl(
+ const std::shared_ptr<C2Allocator> &allocator, bool linear)
+ : mAllocator(allocator), mLinear(linear) {}
+
+Accessor::Impl::~Impl() {
+}
+
+ResultStatus Accessor::Impl::connect(
+ const sp<Accessor> &accessor, sp<Connection> *connection,
+ ConnectionId *pConnectionId, const QueueDescriptor** fmqDescPtr) {
+ sp<Connection> newConnection = new Connection();
+ ResultStatus status = ResultStatus::CRITICAL_ERROR;
+ if (newConnection) {
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ ConnectionId id = (int64_t)sPid << 32 | sSeqId;
+ status = mBufferPool.mObserver.open(id, fmqDescPtr);
+ if (status == ResultStatus::OK) {
+ newConnection->initialize(accessor, id);
+ *connection = newConnection;
+ *pConnectionId = id;
+ ++sSeqId;
+ }
+ }
+ return status;
+}
+
+ResultStatus Accessor::Impl::close(ConnectionId connectionId) {
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+ mBufferPool.handleClose(connectionId);
+ mBufferPool.mObserver.close(connectionId);
+ return ResultStatus::OK;
+}
+
+ResultStatus Accessor::Impl::allocate(
+ ConnectionId connectionId, const std::vector<uint8_t>& params,
+ BufferId *bufferId, const native_handle_t** handle) {
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+ ResultStatus status = ResultStatus::OK;
+ if (!mBufferPool.getFreeBuffer(params, bufferId, handle)) {
+ if (mLinear) {
+ status = mBufferPool.getNewLinearBuffer(
+ mAllocator, params, bufferId, handle);
+ } else {
+ status = mBufferPool.getNewGraphicBuffer(
+ mAllocator, params, bufferId, handle);
+ }
+ ALOGV("create a buffer %d : %u %p",
+ status == ResultStatus::OK, *bufferId, *handle);
+ }
+ if (status == ResultStatus::OK) {
+ // TODO: handle ownBuffer failure
+ mBufferPool.handleOwnBuffer(connectionId, *bufferId);
+ }
+ return status;
+}
+
+ResultStatus Accessor::Impl::fetch(
+ ConnectionId connectionId, TransactionId transactionId,
+ BufferId bufferId, const native_handle_t** handle) {
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+ auto found = mBufferPool.mTransactions.find(transactionId);
+ if (found != mBufferPool.mTransactions.end() &&
+ contains(&mBufferPool.mPendingTransactions,
+ connectionId, transactionId)) {
+ if (found->second->mSenderValidated &&
+ found->second->mStatus == BufferStatus::TRANSFER_FROM &&
+ found->second->mBufferId == bufferId) {
+ found->second->mStatus = BufferStatus::TRANSFER_FETCH;
+ auto bufferIt = mBufferPool.mBuffers.find(bufferId);
+ if (bufferIt != mBufferPool.mBuffers.end()) {
+ *handle = bufferIt->second->handle();
+ return ResultStatus::OK;
+ }
+ }
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+void Accessor::Impl::sync() {
+ // TODO: periodic jobs
+ // transaction timeout, buffer cacheing TTL handling
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+}
+
+Accessor::Impl::Impl::BufferPool::BufferPool()
+ : mTimestampUs(getTimestampNow()), mSeq(0) {}
+
+bool Accessor::Impl::BufferPool::handleOwnBuffer(
+ ConnectionId connectionId, BufferId bufferId) {
+
+ bool added = insert(&mUsingBuffers, connectionId, bufferId);
+ if (added) {
+ auto iter = mBuffers.find(bufferId);
+ iter->second->mOwnerCount++;
+ }
+ insert(&mUsingConnections, bufferId, connectionId);
+ return added;
+}
+
+bool Accessor::Impl::BufferPool::handleReleaseBuffer(
+ ConnectionId connectionId, BufferId bufferId) {
+ bool deleted = erase(&mUsingBuffers, connectionId, bufferId);
+ if (deleted) {
+ auto iter = mBuffers.find(bufferId);
+ iter->second->mOwnerCount--;
+ if (iter->second->mOwnerCount == 0 &&
+ iter->second->mTransactionCount == 0) {
+ mFreeBuffers.insert(bufferId);
+ }
+ }
+ erase(&mUsingConnections, bufferId, connectionId);
+ ALOGV("release buffer %u : %d", bufferId, deleted);
+ return deleted;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferTo(const BufferStatusMessage &message) {
+ auto completed = mCompletedTransactions.find(
+ message.transactionId);
+ if (completed != mCompletedTransactions.end()) {
+ // already completed
+ mCompletedTransactions.erase(completed);
+ return true;
+ }
+ // the buffer should exist and be owned.
+ auto bufferIter = mBuffers.find(message.bufferId);
+ if (bufferIter == mBuffers.end() ||
+ !contains(&mUsingBuffers, message.connectionId, message.bufferId)) {
+ return false;
+ }
+ auto found = mTransactions.find(message.transactionId);
+ if (found != mTransactions.end()) {
+ // transfer_from was received earlier.
+ found->second->mSender = message.connectionId;
+ found->second->mSenderValidated = true;
+ return true;
+ }
+ // TODO: verify there is target connection Id
+ mTransactions.insert(std::make_pair(
+ message.transactionId,
+ std::make_unique<TransactionStatus>(message, mTimestampUs)));
+ insert(&mPendingTransactions, message.targetConnectionId,
+ message.transactionId);
+ bufferIter->second->mTransactionCount++;
+ return true;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferFrom(const BufferStatusMessage &message) {
+ auto found = mTransactions.find(message.transactionId);
+ if (found == mTransactions.end()) {
+ // TODO: is it feasible to check ownership here?
+ mTransactions.insert(std::make_pair(
+ message.transactionId,
+ std::make_unique<TransactionStatus>(message, mTimestampUs)));
+ insert(&mPendingTransactions, message.connectionId,
+ message.transactionId);
+ auto bufferIter = mBuffers.find(message.bufferId);
+ bufferIter->second->mTransactionCount++;
+ } else {
+ if (message.connectionId == found->second->mReceiver) {
+ found->second->mStatus = BufferStatus::TRANSFER_FROM;
+ }
+ }
+ return true;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferResult(const BufferStatusMessage &message) {
+ auto found = mTransactions.find(message.transactionId);
+ if (found != mTransactions.end()) {
+ bool deleted = erase(&mPendingTransactions, message.connectionId,
+ message.transactionId);
+ if (deleted) {
+ if (!found->second->mSenderValidated) {
+ mCompletedTransactions.insert(message.transactionId);
+ }
+ auto bufferIter = mBuffers.find(message.bufferId);
+ if (message.newStatus == BufferStatus::TRANSFER_OK) {
+ handleOwnBuffer(message.connectionId, message.bufferId);
+ }
+ bufferIter->second->mTransactionCount--;
+ if (bufferIter->second->mOwnerCount == 0
+ && bufferIter->second->mTransactionCount == 0) {
+ mFreeBuffers.insert(message.bufferId);
+ }
+ }
+ ALOGV("transfer finished %" PRIu64 " %u - %d", message.transactionId,
+ message.bufferId, deleted);
+ return deleted;
+ }
+ ALOGV("transfer not found %" PRIu64 " %u", message.transactionId,
+ message.bufferId);
+ return false;
+}
+
+void Accessor::Impl::BufferPool::processStatusMessages() {
+ std::vector<BufferStatusMessage> messages;
+ mObserver.getBufferStatusChanges(messages);
+ mTimestampUs = getTimestampNow();
+ for (BufferStatusMessage& message: messages) {
+ bool ret = false;
+ switch (message.newStatus) {
+ case BufferStatus::NOT_USED:
+ ret = handleReleaseBuffer(
+ message.connectionId, message.bufferId);
+ break;
+ case BufferStatus::USED:
+ // not happening
+ break;
+ case BufferStatus::TRANSFER_TO:
+ ret = handleTransferTo(message);
+ break;
+ case BufferStatus::TRANSFER_FROM:
+ ret = handleTransferFrom(message);
+ break;
+ case BufferStatus::TRANSFER_TIMEOUT:
+ // TODO
+ break;
+ case BufferStatus::TRANSFER_LOST:
+ // TODO
+ break;
+ case BufferStatus::TRANSFER_FETCH:
+ // not happening
+ break;
+ case BufferStatus::TRANSFER_OK:
+ case BufferStatus::TRANSFER_ERROR:
+ ret = handleTransferResult(message);
+ break;
+ }
+ if (ret == false) {
+ ALOGW("buffer status message processing failure - message : %d "
+ "connection : %" PRId64,
+ message.newStatus, message.connectionId);
+ }
+ }
+ messages.clear();
+}
+
+bool Accessor::Impl::BufferPool::handleClose(ConnectionId connectionId) {
+ // Cleaning buffers
+ auto buffers = mUsingBuffers.find(connectionId);
+ if (buffers != mUsingBuffers.end()) {
+ for (const BufferId& bufferId : buffers->second) {
+ bool deleted = erase(&mUsingConnections, bufferId, connectionId);
+ if (deleted) {
+ auto bufferIter = mBuffers.find(bufferId);
+ bufferIter->second->mOwnerCount--;
+ if (bufferIter->second->mOwnerCount == 0 &&
+ bufferIter->second->mTransactionCount == 0) {
+ // TODO: handle freebuffer insert fail
+ mFreeBuffers.insert(bufferId);
+ }
+ }
+ }
+ mUsingBuffers.erase(buffers);
+ }
+
+ // Cleaning transactions
+ auto pending = mPendingTransactions.find(connectionId);
+ if (pending != mPendingTransactions.end()) {
+ for (const TransactionId& transactionId : pending->second) {
+ auto iter = mTransactions.find(transactionId);
+ if (iter != mTransactions.end()) {
+ if (!iter->second->mSenderValidated) {
+ mCompletedTransactions.insert(transactionId);
+ }
+ BufferId bufferId = iter->second->mBufferId;
+ auto bufferIter = mBuffers.find(bufferId);
+ bufferIter->second->mTransactionCount--;
+ if (bufferIter->second->mOwnerCount == 0 &&
+ bufferIter->second->mTransactionCount == 0) {
+ // TODO: handle freebuffer insert fail
+ mFreeBuffers.insert(bufferId);
+ }
+ mTransactions.erase(iter);
+ }
+ }
+ }
+ return true;
+}
+
+bool Accessor::Impl::BufferPool::getFreeBuffer(
+ const std::vector<uint8_t> ¶ms, BufferId *pId,
+ const native_handle_t** handle) {
+ auto bufferIt = mFreeBuffers.begin();
+ for (;bufferIt != mFreeBuffers.end(); ++bufferIt) {
+ BufferId bufferId = *bufferIt;
+ if (mBuffers[bufferId]->isRecyclable(params)) {
+ break;
+ }
+ }
+ if (bufferIt != mFreeBuffers.end()) {
+ BufferId id = *bufferIt;
+ mFreeBuffers.erase(bufferIt);
+ *handle = mBuffers[id]->handle();
+ *pId = id;
+ ALOGV("recycle a buffer %u %p", id, *handle);
+ return true;
+ }
+ return false;
+}
+
+ResultStatus Accessor::Impl::BufferPool::getNewLinearBuffer(
+ const std::shared_ptr<C2Allocator> &allocator,
+ const std::vector<uint8_t> ¶ms, BufferId *pId,
+ const native_handle_t** handle) {
+ union LinearParam {
+ struct {
+ uint32_t capacity;
+ C2MemoryUsage usage;
+ } data;
+ uint8_t array[0];
+ LinearParam() : data{0, {0, 0}} {}
+ } linearParam;
+ memcpy(&linearParam, params.data(),
+ std::min(sizeof(linearParam), params.size()));
+ std::shared_ptr<C2LinearAllocation> linearAlloc;
+ c2_status_t status = allocator->newLinearAllocation(
+ linearParam.data.capacity, linearParam.data.usage, &linearAlloc);
+ if (status == C2_OK) {
+ BufferId bufferId = mSeq++;
+ std::unique_ptr<InternalBuffer> buffer =
+ std::make_unique<InternalBuffer>(
+ bufferId, linearAlloc, params);
+ if (buffer) {
+ auto res = mBuffers.insert(std::make_pair(
+ bufferId, std::move(buffer)));
+ if (res.second) {
+ *handle = linearAlloc->handle();
+ *pId = bufferId;
+ return ResultStatus::OK;
+ }
+ }
+ return ResultStatus::NO_MEMORY;
+ }
+ // TODO: map C2 error code
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::Impl::BufferPool::getNewGraphicBuffer(
+ const std::shared_ptr<C2Allocator> &allocator,
+ const std::vector<uint8_t> ¶ms, BufferId *pId,
+ const native_handle_t** handle) {
+ union GraphicParam {
+ struct {
+ uint32_t width;
+ uint32_t height;
+ uint32_t format;
+ C2MemoryUsage usage;
+ } data;
+ uint8_t array[0];
+ GraphicParam() : data{0, 0, 0, {0, 0}} {}
+ } graphicParam;
+ memcpy(&graphicParam, params.data(),
+ std::min(sizeof(graphicParam), params.size()));
+ std::shared_ptr<C2GraphicAllocation> graphicAlloc;
+ c2_status_t status = allocator->newGraphicAllocation(
+ graphicParam.data.width, graphicParam.data.height,
+ graphicParam.data.format, graphicParam.data.usage, &graphicAlloc);
+ if (status == C2_OK) {
+ BufferId bufferId = mSeq;
+ std::unique_ptr<InternalBuffer> buffer =
+ std::make_unique<InternalBuffer>(
+ bufferId, graphicAlloc, params);
+ if (buffer) {
+ auto res = mBuffers.insert(std::make_pair(
+ bufferId, std::move(buffer)));
+ if (res.second) {
+ *handle = graphicAlloc->handle();
+ *pId = bufferId;
+ return ResultStatus::OK;
+ }
+ }
+ return ResultStatus::NO_MEMORY;
+ }
+ // TODO: map C2 error code
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.h b/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.h
new file mode 100644
index 0000000..92d926c
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
+
+#include <map>
+#include <set>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+struct InternalBuffer;
+struct TransactionStatus;
+
+/**
+ * An implementation of a buffer pool accessor(or a buffer pool implementation.) */
+class Accessor::Impl {
+public:
+ Impl(const std::shared_ptr<C2Allocator> &allocator, bool linear);
+
+ ~Impl();
+
+ ResultStatus connect(
+ const sp<Accessor> &accessor, sp<Connection> *connection,
+ ConnectionId *pConnectionId, const QueueDescriptor** fmqDescPtr);
+
+ ResultStatus close(ConnectionId connectionId);
+
+ ResultStatus allocate(ConnectionId connectionId,
+ const std::vector<uint8_t>& params,
+ BufferId *bufferId,
+ const native_handle_t** handle);
+
+ ResultStatus fetch(ConnectionId connectionId,
+ TransactionId transactionId,
+ BufferId bufferId,
+ const native_handle_t** handle);
+
+ /** Processes pending buffer status messages */
+ void sync();
+
+private:
+ // ConnectionId = pid : (timestamp_created + seqId)
+ // in order to guarantee uniqueness for each connection
+ static uint32_t sSeqId;
+ static int32_t sPid;
+
+ const std::shared_ptr<C2Allocator> mAllocator;
+ bool mLinear;
+
+ /**
+ * Buffer pool implementation.
+ *
+ * Handles buffer status messages. Handles buffer allocation/recycling.
+ * Handles buffer transfer between buffer pool clients.
+ */
+ struct BufferPool {
+ private:
+ std::mutex mMutex;
+ int64_t mTimestampUs;
+ BufferId mSeq;
+ BufferStatusObserver mObserver;
+
+ std::map<ConnectionId, std::set<BufferId>> mUsingBuffers;
+ std::map<BufferId, std::set<ConnectionId>> mUsingConnections;
+
+ std::map<ConnectionId, std::set<TransactionId>> mPendingTransactions;
+ // Transactions completed before TRANSFER_TO message arrival.
+ // Fetch does not occur for the transactions.
+ // Only transaction id is kept for the transactions in short duration.
+ std::set<TransactionId> mCompletedTransactions;
+ // Currently active(pending) transations' status & information.
+ std::map<TransactionId, std::unique_ptr<TransactionStatus>>
+ mTransactions;
+
+ std::map<BufferId, std::unique_ptr<InternalBuffer>> mBuffers;
+ std::set<BufferId> mFreeBuffers;
+
+ public:
+ /** Creates a buffer pool. */
+ BufferPool();
+
+ /**
+ * Processes all pending buffer status messages, and returns the result.
+ * Each status message is handled by methods with 'handle' prefix.
+ */
+ void processStatusMessages();
+
+ /**
+ * Handles a buffer being owned by a connection.
+ *
+ * @param connectionId the id of the buffer owning connection.
+ * @param bufferId the id of the buffer.
+ *
+ * @return {@code true} when the buffer is owned,
+ * {@code false} otherwise.
+ */
+ bool handleOwnBuffer(ConnectionId connectionId, BufferId bufferId);
+
+ /**
+ * Handles a buffer being released by a connection.
+ *
+ * @param connectionId the id of the buffer owning connection.
+ * @param bufferId the id of the buffer.
+ *
+ * @return {@code true} when the buffer ownership is released,
+ * {@code false} otherwise.
+ */
+ bool handleReleaseBuffer(ConnectionId connectionId, BufferId bufferId);
+
+ /**
+ * Handles a transfer transaction start message from the sender.
+ *
+ * @param message a buffer status message for the transaction.
+ *
+ * @result {@code true} when transfer_to message is acknowledged,
+ * {@code false} otherwise.
+ */
+ bool handleTransferTo(const BufferStatusMessage &message);
+
+ /**
+ * Handles a transfer transaction being acked by the receiver.
+ *
+ * @param message a buffer status message for the transaction.
+ *
+ * @result {@code true} when transfer_from message is acknowledged,
+ * {@code false} otherwise.
+ */
+ bool handleTransferFrom(const BufferStatusMessage &message);
+
+ /**
+ * Handles a transfer transaction result message from the receiver.
+ *
+ * @param message a buffer status message for the transaction.
+ *
+ * @result {@code true} when the exisitng transaction is finished,
+ * {@code false} otherwise.
+ */
+ bool handleTransferResult(const BufferStatusMessage &message);
+
+ /**
+ * Handles a connection being closed, and returns the result. All the
+ * buffers and transactions owned by the connection will be cleaned up.
+ * The related FMQ will be cleaned up too.
+ *
+ * @param connectionId the id of the connection.
+ *
+ * @result {@code true} when the connection existed,
+ * {@code false} otherwise.
+ */
+ bool handleClose(ConnectionId connectionId);
+
+ /**
+ * Recycles a existing free buffer if it is possible.
+ *
+ * @param params the allocation parameters.
+ * @param pId the id of the recycled buffer.
+ * @param handle the native handle of the recycled buffer.
+ *
+ * @return {@code true} when a buffer is recycled, {@code false}
+ * otherwise.
+ */
+ bool getFreeBuffer(const std::vector<uint8_t> ¶ms, BufferId *pId,
+ const native_handle_t **handle);
+
+ /**
+ * Creates a new linear buffer.
+ *
+ * @param allocator the linear buffer allocator
+ * @param params the allocator parameters
+ * @param pId the buffer id for the newly allocated buffer.
+ * @param handle the native handle for the newly allocated buffer.
+ *
+ * @return OK when a linear allocation is successfully allocated.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus getNewLinearBuffer(
+ const std::shared_ptr<C2Allocator> &allocator,
+ const std::vector<uint8_t> ¶ms, BufferId *pId,
+ const native_handle_t **handle);
+
+ /**
+ * Creates a new graphic buffer.
+ *
+ * @param allocator the graphic buffer allocator
+ * @param params the allocator parameters
+ * @param pId the buffer id for the newly allocated buffer.
+ * @param handle the native handle for the newly allocated buffer.
+ *
+ * @return OK when a graphic allocation is successfully allocated.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus getNewGraphicBuffer(
+ const std::shared_ptr<C2Allocator> &allocator,
+ const std::vector<uint8_t> ¶ms, BufferId *pId,
+ const native_handle_t **handle);
+
+ friend class Accessor::Impl;
+ } mBufferPool;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace ufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
diff --git a/media/libstagefright/codec2/vndk/bufferpool/Android.bp b/media/libstagefright/codec2/vndk/bufferpool/Android.bp
new file mode 100644
index 0000000..f0588d5
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/Android.bp
@@ -0,0 +1,31 @@
+cc_library_shared {
+ name: "android.hardware.media.bufferpool@1.0-impl",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "Accessor.cpp",
+ "AccessorImpl.cpp",
+ "BufferPoolClient.cpp",
+ "BufferStatus.cpp",
+ "ClientManager.cpp",
+ "Connection.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ include_dirs: [
+ "frameworks/av/media/libstagefright/codec2/vndk/bufferpool/include",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "libhidltransport",
+ "liblog",
+ "libstagefright_codec2",
+ "libutils",
+ "android.hardware.media.bufferpool@1.0",
+ ],
+}
diff --git a/media/libstagefright/codec2/vndk/bufferpool/BufferPoolClient.cpp b/media/libstagefright/codec2/vndk/bufferpool/BufferPoolClient.cpp
new file mode 100644
index 0000000..7cb4ba9
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/BufferPoolClient.cpp
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define LOG_TAG "bufferpool"
+//#define LOG_NDEBUG 0
+
+#include <inttypes.h>
+#include <thread>
+#include <utils/Log.h>
+#include "BufferPoolClient.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+static constexpr int64_t kReceiveTimeoutUs = 5000; // 5ms
+static constexpr int kPostMaxRetry = 3;
+static constexpr int kCacheTtlUs = 500000; // TODO: tune
+
+class BufferPoolClient::Impl
+ : public std::enable_shared_from_this<BufferPoolClient::Impl> {
+public:
+ explicit Impl(const sp<Accessor> &accessor);
+
+ explicit Impl(const sp<IAccessor> &accessor);
+
+ bool isValid() {
+ return mValid;
+ }
+
+ ConnectionId getConnectionId() {
+ return mConnectionId;
+ }
+
+ sp<IAccessor> &getAccessor() {
+ return mAccessor;
+ }
+
+ ResultStatus allocate(const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<_C2BlockPoolData> *buffer);
+
+ ResultStatus receive(
+ TransactionId transactionId, BufferId bufferId,
+ int64_t timestampUs, std::shared_ptr<_C2BlockPoolData> *buffer);
+
+ void postBufferRelease(BufferId bufferId);
+
+ bool postSend(
+ BufferId bufferId, ConnectionId receiver,
+ TransactionId *transactionId, int64_t *timestampUs);
+private:
+
+ bool postReceive(
+ BufferId bufferId, TransactionId transactionId,
+ int64_t timestampUs);
+
+ bool postReceiveResult(
+ BufferId bufferId, TransactionId transactionId, bool result);
+
+ bool syncReleased();
+
+ ResultStatus allocateBufferHandle(
+ const std::vector<uint8_t>& params, BufferId *bufferId,
+ native_handle_t **handle);
+
+ ResultStatus fetchBufferHandle(
+ TransactionId transactionId, BufferId bufferId,
+ native_handle_t **handle);
+
+
+ struct BlockPoolDataDtor;
+ struct ClientBuffer;
+
+ bool mLocal;
+ bool mValid;
+ sp<IAccessor> mAccessor;
+ sp<Connection> mLocalConnection;
+ sp<IConnection> mRemoteConnection;
+ uint32_t mSeqId;
+ ConnectionId mConnectionId;
+
+ // CachedBuffers
+ struct {
+ std::mutex mLock;
+ bool creating;
+ std::condition_variable mCreateCv;
+ std::map<BufferId, std::unique_ptr<ClientBuffer>> mBuffers;
+ } mCache;
+
+ // FMQ - release notifier
+ struct {
+ std::mutex mLock;
+ // TODO: use only one list?(using one list may dealy sending messages?)
+ std::list<BufferId> mReleasingIds;
+ std::list<BufferId> mReleasedIds;
+ std::unique_ptr<BufferStatusChannel> mStatusChannel;
+ } mReleasing;
+};
+
+struct BufferPoolClient::Impl::BlockPoolDataDtor {
+ BlockPoolDataDtor(const std::shared_ptr<BufferPoolClient::Impl> &impl)
+ : mImpl(impl) {}
+
+ void operator()(_C2BlockPoolData *buffer) {
+ BufferId id = buffer->mId;
+ delete buffer;
+
+ auto impl = mImpl.lock();
+ if (impl && impl->isValid()) {
+ impl->postBufferRelease(id);
+ }
+ }
+ const std::weak_ptr<BufferPoolClient::Impl> mImpl;
+};
+
+struct BufferPoolClient::Impl::ClientBuffer {
+private:
+ bool mInvalidated; // TODO: implement
+ int64_t mExpireUs;
+ bool mHasCache;
+ _C2BlockPoolData mBuffer;
+ std::weak_ptr<_C2BlockPoolData> mCache;
+
+ void updateExpire() {
+ mExpireUs = getTimestampNow() + kCacheTtlUs;
+ }
+
+public:
+ ClientBuffer(BufferId id, native_handle_t *handle)
+ : mInvalidated(false), mHasCache(false), mBuffer(id, handle) {
+ (void)mInvalidated;
+ mExpireUs = getTimestampNow() + kCacheTtlUs;
+ }
+
+ bool expire() const {
+ int64_t now = getTimestampNow();
+ return now >= mExpireUs;
+ }
+
+ bool hasCache() const {
+ return mHasCache;
+ }
+
+ std::shared_ptr<_C2BlockPoolData> fetchCache() {
+ if (mHasCache) {
+ std::shared_ptr<_C2BlockPoolData> cache = mCache.lock();
+ if (cache) {
+ updateExpire();
+ }
+ return cache;
+ }
+ return nullptr;
+ }
+
+ std::shared_ptr<_C2BlockPoolData> createCache(
+ const std::shared_ptr<BufferPoolClient::Impl> &impl) {
+ if (!mHasCache) {
+ // Allocates a raw ptr in order to avoid sending #postBufferRelease
+ // from deleter, in case of native_handle_clone failure.
+ _C2BlockPoolData *ptr = new _C2BlockPoolData(
+ mBuffer.mId, native_handle_clone(mBuffer.mHandle));
+ if (ptr && ptr->mHandle != NULL) {
+ std::shared_ptr<_C2BlockPoolData>
+ cache(ptr, BlockPoolDataDtor(impl));
+ if (cache) {
+ mCache = cache;
+ mHasCache = true;
+ updateExpire();
+ return cache;
+ }
+ }
+ if (ptr) {
+ delete ptr;
+ }
+ }
+ return nullptr;
+ }
+
+ bool onCacheRelease() {
+ if (mHasCache) {
+ // TODO: verify mCache is not valid;
+ mHasCache = false;
+ return true;
+ }
+ return false;
+ }
+};
+
+BufferPoolClient::Impl::Impl(const sp<Accessor> &accessor)
+ : mLocal(true), mAccessor(accessor), mSeqId(0) {
+ mValid = false;
+ const QueueDescriptor *fmqDesc;
+ ResultStatus status = accessor->connect(
+ &mLocalConnection, &mConnectionId, &fmqDesc);
+ if (status == ResultStatus::OK) {
+ mReleasing.mStatusChannel =
+ std::make_unique<BufferStatusChannel>(*fmqDesc);
+ mValid = mReleasing.mStatusChannel &&
+ mReleasing.mStatusChannel->isValid();
+ }
+}
+
+BufferPoolClient::Impl::Impl(const sp<IAccessor> &accessor)
+ : mLocal(false), mAccessor(accessor), mSeqId(0) {
+ mValid = false;
+ bool& valid = mValid;
+ sp<IConnection>& outConnection = mRemoteConnection;
+ ConnectionId& id = mConnectionId;
+ std::unique_ptr<BufferStatusChannel>& outChannel =
+ mReleasing.mStatusChannel;
+ accessor->connect(
+ [&valid, &outConnection, &id, &outChannel]
+ (ResultStatus status, sp<IConnection> connection,
+ ConnectionId connectionId, const QueueDescriptor& desc) {
+ if (status == ResultStatus::OK) {
+ outConnection = connection;
+ id = connectionId;
+ outChannel = std::make_unique<BufferStatusChannel>(desc);
+ if (outChannel && outChannel->isValid()) {
+ valid = true;
+ }
+ }
+ });
+}
+
+ResultStatus BufferPoolClient::Impl::allocate(
+ const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<_C2BlockPoolData> *buffer) {
+ if (!mLocal || !mLocalConnection || !mValid) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ BufferId bufferId;
+ native_handle_t *handle = NULL;
+ buffer->reset();
+ ResultStatus status = allocateBufferHandle(params, &bufferId, &handle);
+ if (status == ResultStatus::OK) {
+ if (handle) {
+ std::unique_lock<std::mutex> lock(mCache.mLock);
+ syncReleased();
+ auto cacheIt = mCache.mBuffers.find(bufferId);
+ if (cacheIt != mCache.mBuffers.end()) {
+ // TODO: verify it is recycled. (not having active ref)
+ mCache.mBuffers.erase(cacheIt);
+ }
+ auto clientBuffer = std::make_unique<ClientBuffer>(
+ bufferId, handle);
+ if (clientBuffer) {
+ auto result = mCache.mBuffers.insert(std::make_pair(
+ bufferId, std::move(clientBuffer)));
+ if (result.second) {
+ *buffer = result.first->second->createCache(
+ shared_from_this());
+ }
+ }
+ }
+ if (!*buffer) {
+ ALOGV("client cache creation failure %d: %" PRId64,
+ handle != NULL, mConnectionId);
+ status = ResultStatus::NO_MEMORY;
+ postBufferRelease(bufferId);
+ }
+ }
+ return status;
+}
+
+ResultStatus BufferPoolClient::Impl::receive(
+ TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
+ std::shared_ptr<_C2BlockPoolData> *buffer) {
+ if (!mValid) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ if (timestampUs != 0) {
+ timestampUs += kReceiveTimeoutUs;
+ }
+ if (!postReceive(bufferId, transactionId, timestampUs)) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ ResultStatus status = ResultStatus::CRITICAL_ERROR;
+ buffer->reset();
+ while(1) {
+ std::unique_lock<std::mutex> lock(mCache.mLock);
+ syncReleased();
+ auto cacheIt = mCache.mBuffers.find(bufferId);
+ if (cacheIt != mCache.mBuffers.end()) {
+ if (cacheIt->second->hasCache()) {
+ *buffer = cacheIt->second->fetchCache();
+ if (!*buffer) {
+ // check transfer time_out
+ lock.unlock();
+ std::this_thread::yield();
+ continue;
+ }
+ ALOGV("client receive from reference %" PRId64, mConnectionId);
+ break;
+ } else {
+ *buffer = cacheIt->second->createCache(shared_from_this());
+ ALOGV("client receive from cache %" PRId64, mConnectionId);
+ break;
+ }
+ } else {
+ if (!mCache.creating) {
+ mCache.creating = true;
+ lock.unlock();
+ native_handle_t* handle = NULL;
+ status = fetchBufferHandle(transactionId, bufferId, &handle);
+ lock.lock();
+ if (status == ResultStatus::OK) {
+ if (handle) {
+ auto clientBuffer = std::make_unique<ClientBuffer>(
+ bufferId, handle);
+ if (clientBuffer) {
+ auto result = mCache.mBuffers.insert(
+ std::make_pair(bufferId, std::move(
+ clientBuffer)));
+ if (result.second) {
+ *buffer = result.first->second->createCache(
+ shared_from_this());
+ }
+ }
+ }
+ if (!*buffer) {
+ status = ResultStatus::NO_MEMORY;
+ }
+ }
+ mCache.creating = false;
+ lock.unlock();
+ mCache.mCreateCv.notify_all();
+ break;
+ }
+ mCache.mCreateCv.wait(lock);
+ }
+ }
+ bool posted = postReceiveResult(bufferId, transactionId,
+ *buffer ? true : false);
+ ALOGV("client receive %" PRId64 " - %u : %s (%d)", mConnectionId, bufferId,
+ *buffer ? "ok" : "fail", posted);
+ if (*buffer) {
+ if (!posted) {
+ buffer->reset();
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ return ResultStatus::OK;
+ }
+ return status;
+}
+
+
+void BufferPoolClient::Impl::postBufferRelease(BufferId bufferId) {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ mReleasing.mReleasingIds.push_back(bufferId);
+ mReleasing.mStatusChannel->postBufferRelease(
+ mConnectionId, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
+}
+
+// TODO: revise ad-hoc posting data structure
+bool BufferPoolClient::Impl::postSend(
+ BufferId bufferId, ConnectionId receiver,
+ TransactionId *transactionId, int64_t *timestampUs) {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ *timestampUs = getTimestampNow();
+ *transactionId = (mConnectionId << 32) | mSeqId++;
+ // TODO: retry, add timeout, target?
+ return mReleasing.mStatusChannel->postBufferStatusMessage(
+ *transactionId, bufferId, BufferStatus::TRANSFER_TO, mConnectionId,
+ receiver, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
+}
+
+bool BufferPoolClient::Impl::postReceive(
+ BufferId bufferId, TransactionId transactionId, int64_t timestampUs) {
+ for (int i = 0; i < kPostMaxRetry; ++i) {
+ std::unique_lock<std::mutex> lock(mReleasing.mLock);
+ int64_t now = getTimestampNow();
+ if (timestampUs == 0 || now < timestampUs) {
+ bool result = mReleasing.mStatusChannel->postBufferStatusMessage(
+ transactionId, bufferId, BufferStatus::TRANSFER_FROM,
+ mConnectionId, -1, mReleasing.mReleasingIds,
+ mReleasing.mReleasedIds);
+ if (result) {
+ return true;
+ }
+ lock.unlock();
+ std::this_thread::yield();
+ } else {
+ mReleasing.mStatusChannel->postBufferStatusMessage(
+ transactionId, bufferId, BufferStatus::TRANSFER_TIMEOUT,
+ mConnectionId, -1, mReleasing.mReleasingIds,
+ mReleasing.mReleasedIds);
+ return false;
+ }
+ }
+ return false;
+}
+
+bool BufferPoolClient::Impl::postReceiveResult(
+ BufferId bufferId, TransactionId transactionId, bool result) {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ // TODO: retry, add timeout
+ return mReleasing.mStatusChannel->postBufferStatusMessage(
+ transactionId, bufferId,
+ result ? BufferStatus::TRANSFER_OK : BufferStatus::TRANSFER_ERROR,
+ mConnectionId, -1, mReleasing.mReleasingIds,
+ mReleasing.mReleasedIds);
+}
+
+// should have mCache.mLock
+bool BufferPoolClient::Impl::syncReleased() {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ if (mReleasing.mReleasingIds.size() > 0) {
+ mReleasing.mStatusChannel->postBufferRelease(
+ mConnectionId, mReleasing.mReleasingIds,
+ mReleasing.mReleasedIds);
+ }
+ if (mReleasing.mReleasedIds.size() > 0) {
+ for (BufferId& id: mReleasing.mReleasedIds) {
+ ALOGV("client release buffer %" PRId64 " - %u", mConnectionId, id);
+ auto found = mCache.mBuffers.find(id);
+ if (found != mCache.mBuffers.end()) {
+ if (!found->second->onCacheRelease()) {
+ // should not happen!
+ ALOGW("client %" PRId64 "cache release status inconsitent!",
+ mConnectionId);
+ }
+ if (found->second->expire()) {
+ ALOGV("client evict buffer from cache %" PRId64 " - %u",
+ mConnectionId, id);
+ mCache.mBuffers.erase(found);
+ }
+ } else {
+ // should not happen!
+ ALOGW("client %" PRId64 "cache status inconsitent!",
+ mConnectionId);
+ }
+ }
+ mReleasing.mReleasedIds.clear();
+ return true;
+ }
+ return false;
+}
+
+ResultStatus BufferPoolClient::Impl::allocateBufferHandle(
+ const std::vector<uint8_t>& params, BufferId *bufferId,
+ native_handle_t** handle) {
+ if (mLocalConnection) {
+ const native_handle_t* allocHandle = NULL;
+ ResultStatus status = mLocalConnection->allocate(
+ params, bufferId, &allocHandle);
+ if (status == ResultStatus::OK) {
+ *handle = native_handle_clone(allocHandle);
+ }
+ ALOGV("client allocate result %" PRId64 "%d : %u clone %p",
+ mConnectionId, status == ResultStatus::OK,
+ *handle ? *bufferId : 0 , *handle);
+ return status;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::Impl::fetchBufferHandle(
+ TransactionId transactionId, BufferId bufferId,
+ native_handle_t **handle) {
+ sp<IConnection> connection;
+ if (mLocal) {
+ connection = mLocalConnection;
+ } else {
+ connection = mRemoteConnection;
+ }
+ ResultStatus status;
+ connection->fetch(
+ transactionId, bufferId,
+ [&status, &handle]
+ (ResultStatus outStatus, Buffer outBuffer) {
+ status = outStatus;
+ if (status == ResultStatus::OK) {
+ *handle = native_handle_clone(
+ outBuffer.buffer.getNativeHandle());
+ }
+ });
+ return status;
+}
+
+
+BufferPoolClient::BufferPoolClient(const sp<Accessor> &accessor) {
+ mImpl = std::make_shared<Impl>(accessor);
+}
+
+BufferPoolClient::BufferPoolClient(const sp<IAccessor> &accessor) {
+ mImpl = std::make_shared<Impl>(accessor);
+}
+
+BufferPoolClient::~BufferPoolClient() {
+ // TODO: how to handle orphaned buffers?
+}
+
+bool BufferPoolClient::isValid() {
+ return mImpl && mImpl->isValid();
+}
+
+ConnectionId BufferPoolClient::getConnectionId() {
+ if (isValid()) {
+ return mImpl->getConnectionId();
+ }
+ return -1;
+}
+
+ResultStatus BufferPoolClient::getAccessor(sp<IAccessor> *accessor) {
+ if (isValid()) {
+ *accessor = mImpl->getAccessor();
+ return ResultStatus::OK;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::allocate(
+ const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<_C2BlockPoolData> *buffer) {
+ if (isValid()) {
+ return mImpl->allocate(params, buffer);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::receive(
+ TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
+ std::shared_ptr<_C2BlockPoolData> *buffer) {
+ if (isValid()) {
+ return mImpl->receive(transactionId, bufferId, timestampUs, buffer);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::postSend(
+ ConnectionId receiverId,
+ const std::shared_ptr<_C2BlockPoolData> &buffer,
+ TransactionId *transactionId,
+ int64_t *timestampUs) {
+ if (isValid()) {
+ bool result = mImpl->postSend(
+ buffer->mId, receiverId, transactionId, timestampUs);
+ return result ? ResultStatus::OK : ResultStatus::CRITICAL_ERROR;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/codec2/vndk/bufferpool/BufferPoolClient.h b/media/libstagefright/codec2/vndk/bufferpool/BufferPoolClient.h
new file mode 100644
index 0000000..a6111b5
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/BufferPoolClient.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
+
+#include <memory>
+#include <cutils/native_handle.h>
+#include <android/hardware/media/bufferpool/1.0/IAccessor.h>
+#include <android/hardware/media/bufferpool/1.0/IConnection.h>
+#include <BufferPoolTypes.h>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::media::bufferpool::V1_0::IAccessor;
+using ::android::hardware::media::bufferpool::V1_0::IConnection;
+using ::android::hardware::media::bufferpool::V1_0::ResultStatus;
+using ::android::sp;
+
+/**
+ * A buffer pool client for a buffer pool. For a specific buffer pool, at most
+ * one buffer pool client exists per process. This class will not be exposed
+ * outside. A buffer pool client will be used via ClientManager.
+ */
+class BufferPoolClient {
+public:
+ /**
+ * Creates a buffer pool client from a local buffer pool
+ * (via ClientManager#create).
+ */
+ explicit BufferPoolClient(const sp<Accessor> &accessor);
+
+ /**
+ * Creates a buffer pool client from a remote buffer pool
+ * (via ClientManager#registerSender).
+ * Note: A buffer pool client created with remote buffer pool cannot
+ * allocate a buffer.
+ */
+ explicit BufferPoolClient(const sp<IAccessor> &accessor);
+
+ /** Destructs a buffer pool client. */
+ ~BufferPoolClient();
+
+private:
+ bool isValid();
+
+ ConnectionId getConnectionId();
+
+ ResultStatus getAccessor(sp<IAccessor> *accessor);
+
+ ResultStatus allocate(const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<_C2BlockPoolData> *buffer);
+
+ ResultStatus receive(TransactionId transactionId,
+ BufferId bufferId,
+ int64_t timestampUs,
+ std::shared_ptr<_C2BlockPoolData> *buffer);
+
+ ResultStatus postSend(ConnectionId receiver,
+ const std::shared_ptr<_C2BlockPoolData> &buffer,
+ TransactionId *transactionId,
+ int64_t *timestampUs);
+
+ class Impl;
+ std::shared_ptr<Impl> mImpl;
+
+ friend struct ClientManager;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
diff --git a/media/libstagefright/codec2/vndk/bufferpool/BufferStatus.cpp b/media/libstagefright/codec2/vndk/bufferpool/BufferStatus.cpp
new file mode 100644
index 0000000..4fc88c6
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/BufferStatus.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define LOG_TAG "bufferpool"
+//#define LOG_NDEBUG 0
+
+#include <inttypes.h>
+#include <time.h>
+#include "BufferStatus.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+int64_t getTimestampNow() {
+ int64_t stamp;
+ struct timespec ts;
+ // TODO: CLOCK_MONOTONIC_COARSE?
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ stamp = ts.tv_nsec / 1000;
+ stamp += (ts.tv_sec * 1000000LL);
+ return stamp;
+}
+
+static constexpr int kNumElementsInQueue = 1024*16;
+
+ResultStatus BufferStatusObserver::open(
+ ConnectionId id, const QueueDescriptor** fmqDescPtr) {
+ if (mBufferStatusQueues.find(id) != mBufferStatusQueues.end()) {
+ // TODO: id collision log?
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ std::unique_ptr<BufferStatusQueue> queue =
+ std::make_unique<BufferStatusQueue>(kNumElementsInQueue);
+ if (!queue || queue->isValid() == false) {
+ *fmqDescPtr = NULL;
+ return ResultStatus::NO_MEMORY;
+ } else {
+ *fmqDescPtr = queue->getDesc();
+ }
+ auto result = mBufferStatusQueues.insert(
+ std::make_pair(id, std::move(queue)));
+ if (!result.second) {
+ *fmqDescPtr = NULL;
+ return ResultStatus::NO_MEMORY;
+ }
+ return ResultStatus::OK;
+}
+
+ResultStatus BufferStatusObserver::close(ConnectionId id) {
+ if (mBufferStatusQueues.find(id) == mBufferStatusQueues.end()) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ mBufferStatusQueues.erase(id);
+ return ResultStatus::OK;
+}
+
+void BufferStatusObserver::getBufferStatusChanges(std::vector<BufferStatusMessage> &messages) {
+ for (auto it = mBufferStatusQueues.begin(); it != mBufferStatusQueues.end(); ++it) {
+ BufferStatusMessage message;
+ size_t avail = it->second->availableToRead();
+ while (avail > 0) {
+ if (!it->second->read(&message, 1)) {
+ // Since avaliable # of reads are already confiremd,
+ // this should not happen.
+ // TODO: error handling (supurious client?)
+ ALOGW("FMQ message cannot be read from %" PRId64, it->first);
+ return;
+ }
+ message.connectionId = it->first;
+ messages.push_back(message);
+ --avail;
+ }
+ }
+}
+
+BufferStatusChannel::BufferStatusChannel(
+ const QueueDescriptor &fmqDesc) {
+ std::unique_ptr<BufferStatusQueue> queue =
+ std::make_unique<BufferStatusQueue>(fmqDesc);
+ if (!queue || queue->isValid() == false) {
+ mValid = false;
+ return;
+ }
+ mValid = true;
+ mBufferStatusQueue = std::move(queue);
+}
+
+bool BufferStatusChannel::isValid() {
+ return mValid;
+}
+
+void BufferStatusChannel::postBufferRelease(
+ ConnectionId connectionId,
+ std::list<BufferId> &pending, std::list<BufferId> &posted) {
+ if (mValid && pending.size() > 0) {
+ size_t avail = mBufferStatusQueue->availableToWrite();
+ avail = std::min(avail, pending.size());
+ BufferStatusMessage message;
+ for (size_t i = 0 ; i < avail; ++i) {
+ BufferId id = pending.front();
+ message.newStatus = BufferStatus::NOT_USED;
+ message.bufferId = id;
+ message.connectionId = connectionId;
+ if (!mBufferStatusQueue->write(&message, 1)) {
+ // Since avaliable # of writes are already confiremd,
+ // this should not happen.
+ // TODO: error handing?
+ ALOGW("FMQ message cannot be sent from %" PRId64, connectionId);
+ return;
+ }
+ pending.pop_front();
+ posted.push_back(id);
+ }
+ }
+}
+
+bool BufferStatusChannel::postBufferStatusMessage(
+ TransactionId transactionId, BufferId bufferId,
+ BufferStatus status, ConnectionId connectionId, ConnectionId targetId,
+ std::list<BufferId> &pending, std::list<BufferId> &posted) {
+ if (mValid) {
+ size_t avail = mBufferStatusQueue->availableToWrite();
+ size_t numPending = pending.size();
+ if (avail >= numPending + 1) {
+ BufferStatusMessage release, message;
+ for (size_t i = 0; i < numPending; ++i) {
+ BufferId id = pending.front();
+ release.newStatus = BufferStatus::NOT_USED;
+ release.bufferId = id;
+ release.connectionId = connectionId;
+ if (!mBufferStatusQueue->write(&release, 1)) {
+ // Since avaliable # of writes are already confiremd,
+ // this should not happen.
+ // TODO: error handling?
+ ALOGW("FMQ message cannot be sent from %" PRId64,
+ connectionId);
+ return false;
+ }
+ pending.pop_front();
+ posted.push_back(id);
+ }
+ message.transactionId = transactionId;
+ message.bufferId = bufferId;
+ message.newStatus = status;
+ message.connectionId = connectionId;
+ message.targetConnectionId = targetId;
+ // TODO : timesatamp
+ message.timestampUs = 0;
+ if (!mBufferStatusQueue->write(&message, 1)) {
+ // Since avaliable # of writes are already confiremd,
+ // this should not happen.
+ ALOGW("FMQ message cannot be sent from %" PRId64, connectionId);
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
diff --git a/media/libstagefright/codec2/vndk/bufferpool/BufferStatus.h b/media/libstagefright/codec2/vndk/bufferpool/BufferStatus.h
new file mode 100644
index 0000000..82303cb
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/BufferStatus.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
+
+#include <android/hardware/media/bufferpool/1.0/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <memory>
+#include <mutex>
+#include <vector>
+#include <list>
+#include <BufferPoolTypes.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+/** Returns monotonic timestamp in Us since fixed point in time. */
+int64_t getTimestampNow();
+
+/**
+ * A collection of FMQ for a buffer pool. buffer ownership/status change
+ * messages are sent via the FMQs from the clients.
+ */
+class BufferStatusObserver {
+private:
+ std::map<ConnectionId, std::unique_ptr<BufferStatusQueue>>
+ mBufferStatusQueues;
+
+public:
+ /** Creates an FMQ for the specified connection(client).
+ *
+ * @param connectionId connection Id of the specified client.
+ * @param fmqDescPtr double ptr of created FMQ's descriptor.
+ *
+ * @return OK if FMQ is created successfully.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus open(ConnectionId id, const QueueDescriptor** fmqDescPtr);
+
+ /** Closes an FMQ for the specified connection(client).
+ *
+ * @param connectionId connection Id of the specified client.
+ *
+ * @return OK if the specified connection is closed successfully.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus close(ConnectionId id);
+
+ /** Retrieves all pending FMQ buffer status messages from clients.
+ *
+ * @param messages retrieved pending messages.
+ */
+ void getBufferStatusChanges(std::vector<BufferStatusMessage> &messages);
+};
+
+/**
+ * An FMQ for a buffer pool client. buffer ownership/status change messages
+ * are sent via the fmq to the buffer pool.
+ */
+class BufferStatusChannel {
+private:
+ bool mValid;
+ std::unique_ptr<BufferStatusQueue> mBufferStatusQueue;
+
+public:
+ /**
+ * Connects to an FMQ from a descriptor of the created FMQ.
+ *
+ * @param fmqDesc Descriptor of the created FMQ.
+ */
+ BufferStatusChannel(const QueueDescriptor &fmqDesc);
+
+ /** Returns whether the FMQ is connected successfully. */
+ bool isValid();
+
+ /**
+ * Posts a buffer release message to the buffer pool.
+ *
+ * @param connectionId connection Id of the client.
+ * @param pending currently pending buffer release messages.
+ * @param posted posted buffer release messages.
+ */
+ void postBufferRelease(
+ ConnectionId connectionId,
+ std::list<BufferId> &pending, std::list<BufferId> &posted);
+
+ /**
+ * Posts a buffer status message regarding the specified buffer
+ * transfer transaction.
+ *
+ * @param transactionId Id of the specified transaction.
+ * @param bufferId buffer Id of the specified transaction.
+ * @param status new status of the buffer.
+ * @param connectionId connection Id of the client.
+ * @param targetId connection Id of the receiver(only when the sender
+ * posts a status message).
+ * @param pending currently pending buffer release messages.
+ * @param posted posted buffer release messages.
+ *
+ * @return {@code true} when the specified message is posted,
+ * {@code false} otherwise.
+ */
+ bool postBufferStatusMessage(
+ TransactionId transactionId,
+ BufferId bufferId,
+ BufferStatus status,
+ ConnectionId connectionId,
+ ConnectionId targetId,
+ std::list<BufferId> &pending, std::list<BufferId> &posted);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
diff --git a/media/libstagefright/codec2/vndk/bufferpool/ClientManager.cpp b/media/libstagefright/codec2/vndk/bufferpool/ClientManager.cpp
new file mode 100644
index 0000000..97efee4
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/ClientManager.cpp
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <ClientManager.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include "BufferPoolClient.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+static constexpr int64_t kRegisterTimeoutUs = 500000; // 0.5 sec
+
+class ClientManager::Impl {
+public:
+ Impl();
+
+ ResultStatus registerSender(const sp<IAccessor> &accessor,
+ ConnectionId *pConnectionId);
+
+ ResultStatus create(const std::shared_ptr<C2Allocator> &allocator,
+ bool linear,
+ ConnectionId *pConnectionId);
+
+ ResultStatus close(ConnectionId connectionId);
+
+ ResultStatus allocate(ConnectionId connectionId,
+ const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<_C2BlockPoolData> *buffer);
+
+ ResultStatus receive(ConnectionId connectionId,
+ TransactionId transactionId,
+ BufferId bufferId,
+ int64_t timestampUs,
+ std::shared_ptr<_C2BlockPoolData> *buffer);
+
+ ResultStatus postSend(ConnectionId connectionId,
+ ConnectionId receiverId,
+ const std::shared_ptr<_C2BlockPoolData> &buffer,
+ TransactionId *transactionId,
+ int64_t *timestampUs);
+
+ ResultStatus getAccessor(ConnectionId connectionId,
+ sp<IAccessor> *accessor);
+
+private:
+ // In order to prevent deadlock between multiple locks,
+ // Always lock ClientCache.lock before locking ActiveClients.lock.
+ struct ClientCache {
+ // This lock is held for brief duration.
+ // Blocking operation is not performed holding the lock.
+ std::mutex mMutex;
+ std::map<const wp<IAccessor>, const std::weak_ptr<BufferPoolClient>>
+ mClients;
+ std::condition_variable mConnectCv;
+ bool mConnecting;
+
+ ClientCache() : mConnecting(false) {}
+ } mCache;
+
+ // Active clients which can be retrieved via ConnectionId
+ struct ActiveClients {
+ // This lock is held for brief duration.
+ // Blocking operation is not performed holding the lock.
+ std::mutex mMutex;
+ std::map<ConnectionId, const std::shared_ptr<BufferPoolClient>>
+ mClients;
+ } mActive;
+
+};
+
+ClientManager::Impl::Impl() {}
+
+ResultStatus ClientManager::Impl::registerSender(
+ const sp<IAccessor> &accessor, ConnectionId *pConnectionId) {
+ int64_t timeoutUs = getTimestampNow() + kRegisterTimeoutUs;
+ do {
+ std::unique_lock<std::mutex> lock(mCache.mMutex);
+ auto it = mCache.mClients.find(accessor);
+ if (it != mCache.mClients.end()) {
+ const std::shared_ptr<BufferPoolClient> client = it->second.lock();
+ if (client) {
+ *pConnectionId = client->getConnectionId();
+ return ResultStatus::ALREADY_EXISTS;
+ }
+ mCache.mClients.erase(it);
+ }
+ if (!mCache.mConnecting) {
+ mCache.mConnecting = true;
+ lock.unlock();
+ ResultStatus result = ResultStatus::OK;
+ const std::shared_ptr<BufferPoolClient> client =
+ std::make_shared<BufferPoolClient>(accessor);
+ lock.lock();
+ if (!client) {
+ result = ResultStatus::NO_MEMORY;
+ } else if (!client->isValid()) {
+ result = ResultStatus::CRITICAL_ERROR;
+ }
+ if (result == ResultStatus::OK) {
+ // TODO: handle insert fail. (malloc fail)
+ const std::weak_ptr<BufferPoolClient> wclient = client;
+ mCache.mClients.insert(std::make_pair(accessor, wclient));
+ ConnectionId conId = client->getConnectionId();
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ mActive.mClients.insert(std::make_pair(conId, client));
+ }
+ *pConnectionId = conId;
+ }
+ mCache.mConnecting = false;
+ lock.unlock();
+ mCache.mConnectCv.notify_all();
+ return result;
+ }
+ mCache.mConnectCv.wait_for(
+ lock, std::chrono::microseconds(kRegisterTimeoutUs));
+ } while (getTimestampNow() < timeoutUs);
+ // TODO: return timeout error
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::Impl::create(
+ const std::shared_ptr<C2Allocator> &allocator,
+ bool linear,
+ ConnectionId *pConnectionId) {
+ const sp<Accessor> accessor = new Accessor(allocator, linear);
+ if (!accessor || !accessor->isValid()) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ std::shared_ptr<BufferPoolClient> client =
+ std::make_shared<BufferPoolClient>(accessor);
+ if (!client || !client->isValid()) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ {
+ // TODO: handle insert fail. (malloc fail)
+ std::lock_guard<std::mutex> lock(mCache.mMutex);
+ const wp<Accessor> waccessor = accessor;
+ const std::weak_ptr<BufferPoolClient> wclient = client;
+ mCache.mClients.insert(std::make_pair(waccessor, wclient));
+ ConnectionId conId = client->getConnectionId();
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ mActive.mClients.insert(std::make_pair(conId, client));
+ }
+ *pConnectionId = conId;
+ }
+ return ResultStatus::OK;
+}
+
+ResultStatus ClientManager::Impl::close(ConnectionId connectionId) {
+ std::lock_guard<std::mutex> lock1(mCache.mMutex);
+ std::lock_guard<std::mutex> lock2(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it != mActive.mClients.end()) {
+ sp<IAccessor> accessor;
+ if (it->second->getAccessor(&accessor) == ResultStatus::OK) {
+ mCache.mClients.erase(accessor);
+ }
+ mActive.mClients.erase(connectionId);
+ return ResultStatus::OK;
+ }
+ return ResultStatus::NOT_FOUND;
+}
+
+ResultStatus ClientManager::Impl::allocate(
+ ConnectionId connectionId, const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<_C2BlockPoolData> *buffer) {
+ std::shared_ptr<BufferPoolClient> client;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ client = it->second;
+ }
+ return client->allocate(params, buffer);
+}
+
+ResultStatus ClientManager::Impl::receive(
+ ConnectionId connectionId, TransactionId transactionId,
+ BufferId bufferId, int64_t timestampUs,
+ std::shared_ptr<_C2BlockPoolData> *buffer) {
+ std::shared_ptr<BufferPoolClient> client;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ client = it->second;
+ }
+ return client->receive(transactionId, bufferId, timestampUs, buffer);
+}
+
+ResultStatus ClientManager::Impl::postSend(
+ ConnectionId connectionId, ConnectionId receiverId,
+ const std::shared_ptr<_C2BlockPoolData> &buffer,
+ TransactionId *transactionId, int64_t *timestampUs) {
+ std::shared_ptr<BufferPoolClient> client;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ client = it->second;
+ }
+ return client->postSend(receiverId, buffer, transactionId, timestampUs);
+}
+
+ResultStatus ClientManager::Impl::getAccessor(
+ ConnectionId connectionId, sp<IAccessor> *accessor) {
+ std::shared_ptr<BufferPoolClient> client;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ client = it->second;
+ }
+ return client->getAccessor(accessor);
+}
+
+// Methods from ::android::hardware::media::bufferpool::V1_0::IClientManager follow.
+Return<void> ClientManager::registerSender(const sp<::android::hardware::media::bufferpool::V1_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) {
+ if (mImpl) {
+ ConnectionId connectionId = -1;
+ ResultStatus status = mImpl->registerSender(bufferPool, &connectionId);
+ _hidl_cb(status, connectionId);
+ } else {
+ _hidl_cb(ResultStatus::CRITICAL_ERROR, -1);
+ }
+ return Void();
+}
+
+// Methods for local use.
+sp<ClientManager> ClientManager::sInstance;
+std::mutex ClientManager::sInstanceLock;
+
+sp<ClientManager> ClientManager::getInstance() {
+ std::lock_guard<std::mutex> lock(sInstanceLock);
+ if (!sInstance) {
+ sInstance = new ClientManager();
+ }
+ return sInstance;
+}
+
+ClientManager::ClientManager() : mImpl(new Impl()) {}
+
+ClientManager::~ClientManager() {
+}
+
+ResultStatus ClientManager::create(
+ const std::shared_ptr<C2Allocator> &allocator,
+ bool linear,
+ ConnectionId *pConnectionId) {
+ if (mImpl) {
+ return mImpl->create(allocator, linear, pConnectionId);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::close(ConnectionId connectionId) {
+ if (mImpl) {
+ return mImpl->close(connectionId);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::allocate(
+ ConnectionId connectionId, const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<_C2BlockPoolData> *buffer) {
+ if (mImpl) {
+ return mImpl->allocate(connectionId, params, buffer);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::receive(
+ ConnectionId connectionId, TransactionId transactionId,
+ BufferId bufferId, int64_t timestampUs,
+ std::shared_ptr<_C2BlockPoolData> *buffer) {
+ if (mImpl) {
+ return mImpl->receive(connectionId, transactionId, bufferId,
+ timestampUs, buffer);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::postSend(
+ ConnectionId connectionId, ConnectionId receiverId,
+ const std::shared_ptr<_C2BlockPoolData> &buffer,
+ TransactionId *transactionId, int64_t* timestampUs) {
+ if (mImpl) {
+ return mImpl->postSend(connectionId, receiverId, buffer,
+ transactionId, timestampUs);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::getAccessor(
+ ConnectionId connectionId, sp<IAccessor> *accessor) {
+ if (mImpl) {
+ return mImpl->getAccessor(connectionId, accessor);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+//IClientManager* HIDL_FETCH_IClientManager(const char* /* name */) {
+// return new ClientManager();
+//}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/codec2/vndk/bufferpool/Connection.cpp b/media/libstagefright/codec2/vndk/bufferpool/Connection.cpp
new file mode 100644
index 0000000..7b385b8
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/Connection.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::bufferpool::V1_0::IConnection follow.
+Return<void> Connection::fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) {
+ ResultStatus status = ResultStatus::CRITICAL_ERROR;
+ if (mInitialized && mAccessor) {
+ const native_handle_t *handle = NULL;
+ status = mAccessor->fetch(
+ mConnectionId, transactionId, bufferId, &handle);
+ if (status == ResultStatus::OK) {
+ _hidl_cb(status, Buffer{bufferId, handle});
+ return Void();
+ }
+ }
+ _hidl_cb(status, Buffer{0, nullptr});
+ return Void();
+}
+
+Connection::Connection() : mInitialized(false), mConnectionId(-1LL) {}
+
+Connection::~Connection() {
+ if (mInitialized && mAccessor) {
+ mAccessor->close(mConnectionId);
+ }
+}
+
+void Connection::initialize(
+ const sp<Accessor>& accessor, ConnectionId connectionId) {
+ if (!mInitialized) {
+ mAccessor = accessor;
+ mConnectionId = connectionId;
+ mInitialized = true;
+ }
+}
+
+ResultStatus Connection::allocate(
+ const std::vector<uint8_t> ¶ms, BufferId *bufferId,
+ const native_handle_t **handle) {
+ if (mInitialized && mAccessor) {
+ return mAccessor->allocate(mConnectionId, params, bufferId, handle);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+
+// Methods from ::android::hidl::base::V1_0::IBase follow.
+
+//IConnection* HIDL_FETCH_IConnection(const char* /* name */) {
+// return new Connection();
+//}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/codec2/vndk/bufferpool/Connection.h b/media/libstagefright/codec2/vndk/bufferpool/Connection.h
new file mode 100644
index 0000000..47ddbcb
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/Connection.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
+
+#include <android/hardware/media/bufferpool/1.0/IConnection.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <BufferPoolTypes.h>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::media::bufferpool::V1_0::implementation::Accessor;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct Connection : public IConnection {
+ // Methods from ::android::hardware::media::bufferpool::V1_0::IConnection follow.
+ Return<void> fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) override;
+
+ /**
+ * Allocates a buffer using the specified parameters. Recycles a buffer if
+ * it is possible. The returned buffer can be transferred to other remote
+ * clients(Connection).
+ *
+ * @param params allocation parameters.
+ * @param bufferId Id of the allocated buffer.
+ * @param handle native handle of the allocated buffer.
+ *
+ * @return OK if a buffer is successfully allocated.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus allocate(const std::vector<uint8_t> ¶ms,
+ BufferId *bufferId, const native_handle_t **handle);
+
+ /** Destructs a connection. */
+ ~Connection();
+
+ /** Creates a connection. */
+ Connection();
+
+ /**
+ * Initializes with the specified buffer pool and the connection id.
+ * The connection id should be unique in the whole system.
+ *
+ * @param accessor the specified buffer pool.
+ * @param connectionId Id.
+ */
+ void initialize(const sp<Accessor> &accessor, ConnectionId connectionId);
+
+private:
+ bool mInitialized;
+ sp<Accessor> mAccessor;
+ ConnectionId mConnectionId;
+};
+
+// FIXME: most likely delete, this is only for passthrough implementations
+// extern "C" IConnection* HIDL_FETCH_IConnection(const char* name);
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
diff --git a/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h b/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h
new file mode 100644
index 0000000..4b7363f
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
+
+#include <android/hardware/media/bufferpool/1.0/types.h>
+#include <cutils/native_handle.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <C2Buffer.h>
+
+struct C2_HIDE _C2BlockPoolData {
+ uint32_t mId; //BufferId
+ native_handle_t *mHandle;
+
+ _C2BlockPoolData() : mId(0), mHandle(NULL) {}
+
+ _C2BlockPoolData(uint32_t id, native_handle_t *handle)
+ : mId(id), mHandle(handle) {}
+
+ ~_C2BlockPoolData() {
+ if (mHandle != NULL) {
+ native_handle_close(mHandle);
+ native_handle_delete(mHandle);
+ }
+ }
+};
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+
+typedef uint32_t BufferId;
+typedef uint64_t TransactionId;
+typedef int64_t ConnectionId;
+
+typedef android::hardware::MessageQueue<BufferStatusMessage, kSynchronizedReadWrite> BufferStatusQueue;
+typedef BufferStatusQueue::Descriptor QueueDescriptor;
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
diff --git a/media/libstagefright/codec2/vndk/bufferpool/include/ClientManager.h b/media/libstagefright/codec2/vndk/bufferpool/include/ClientManager.h
new file mode 100644
index 0000000..412fa59
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/include/ClientManager.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
+
+#include <android/hardware/media/bufferpool/1.0/IClientManager.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <C2Buffer.h>
+#include <memory>
+#include <BufferPoolTypes.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::media::bufferpool::V1_0::IAccessor;
+using ::android::hardware::media::bufferpool::V1_0::ResultStatus;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct ClientManager : public IClientManager {
+ // Methods from ::android::hardware::media::bufferpool::V1_0::IClientManager follow.
+ Return<void> registerSender(const sp<::android::hardware::media::bufferpool::V1_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) override;
+
+ /** Gets an instance. */
+ static sp<ClientManager> getInstance();
+
+ /**
+ * Creates a local connection with a newly created buffer pool.
+ *
+ * @param allocator for new buffer allocation.
+ * @param linear whether the allocator is linear or not.
+ * @param pConnectionId Id of the created connection. This is
+ * system-wide unique.
+ *
+ * @return OK when a buffer pool and a local connection is successfully
+ * created.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus create(const std::shared_ptr<C2Allocator> &allocator,
+ bool linear,
+ ConnectionId *pConnectionId);
+
+ /**
+ * Closes the specified connection.
+ *
+ * @param connectionId The id of the connection.
+ *
+ * @return OK when the connection is closed.
+ * NOT_FOUND when the specified connection was not found.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus close(ConnectionId connectionId);
+
+ /**
+ * Allocates a buffer from the specified connection.
+ *
+ * @param connectionId The id of the connection.
+ * @param params The allocation parameters.
+ * @param buffer The allocated buffer.
+ *
+ * @return OK when a buffer was allocated successfully.
+ * NOT_FOUND when the specified connection was not found.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus allocate(ConnectionId connectionId,
+ const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<_C2BlockPoolData> *buffer);
+
+ /**
+ * Receives a buffer for the transaction.
+ *
+ * @param connectionId The id of the receiving connection.
+ * @param transactionId The id for the transaction.
+ * @param bufferId The id for the buffer.
+ * @param timestampUs The timestamp of the buffer is being sent.
+ * @param buffer The received buffer.
+ *
+ * @return OK when a buffer was received successfully.
+ * NOT_FOUND when the specified connection was not found.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus receive(ConnectionId connectionId,
+ TransactionId transactionId,
+ BufferId bufferId,
+ int64_t timestampUs,
+ std::shared_ptr<_C2BlockPoolData> *buffer);
+
+ /**
+ * Posts a buffer transfer transaction to the buffer pool. Sends a buffer
+ * to other remote clients(connection) after this call has been succeeded.
+ *
+ * @param connectionId The id of the sending connection.
+ * @param receiverId The id of the receiving connection.
+ * @param buffer to transfer
+ * @param transactionId Id of the transfer transaction.
+ * @param timestampUs The timestamp of the buffer transaction is being
+ * posted.
+ *
+ * @return OK when a buffer transaction was posted successfully.
+ * NOT_FOUND when the sending connection was not found.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus postSend(ConnectionId connectionId,
+ ConnectionId receiverId,
+ const std::shared_ptr<_C2BlockPoolData> &buffer,
+ TransactionId *transactionId,
+ int64_t *timestampUs);
+
+ /** Gets a buffer pool for the specified connection.
+ *
+ * @param connectionId The id of the connection.
+ * @param accessor The buffer pool for the specified connection.
+ * @return OK when a buffer pool was found for the connection.
+ * NOT_FOUND when the specified connection was not found.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus getAccessor(ConnectionId connectionId,
+ sp<IAccessor> *accessor);
+
+ /** Destructs the manager of buffer pool clients. */
+ ~ClientManager();
+private:
+ static sp<ClientManager> sInstance;
+ static std::mutex sInstanceLock;
+
+ class Impl;
+ const std::unique_ptr<Impl> mImpl;
+
+ ClientManager();
+};
+
+// FIXME: most likely delete, this is only for passthrough implementations
+// extern "C" IClientManager* HIDL_FETCH_IClientManager(const char* name);
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H