#include #include #include #include #include #include #include #include #include "include/private/dvr/bufferhub_rpc.h" using android::pdx::LocalChannelHandle; using android::pdx::LocalHandle; using android::pdx::Status; using android::pdx::default_transport::ClientChannel; using android::pdx::default_transport::ClientChannelFactory; namespace android { namespace dvr { BufferHubClient::BufferHubClient() : Client(ClientChannelFactory::Create(BufferHubRPC::kClientPath)) {} BufferHubClient::BufferHubClient(LocalChannelHandle channel_handle) : Client(ClientChannel::Create(std::move(channel_handle))) {} bool BufferHubClient::IsValid() const { return IsConnected() && GetChannelHandle().valid(); } LocalChannelHandle BufferHubClient::TakeChannelHandle() { if (IsConnected()) { return std::move(GetChannelHandle()); } else { return {}; } } 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() { if (metadata_header_ != nullptr) { metadata_buffer_.Unlock(); } } Status BufferHubBuffer::CreateConsumer() { Status status = InvokeRemoteMethod(); ALOGE_IF(!status, "BufferHub::CreateConsumer: Failed to create consumer channel: %s", status.GetErrorMessage().c_str()); return status; } int BufferHubBuffer::ImportBuffer() { ATRACE_NAME("BufferHubBuffer::ImportBuffer"); Status> status = InvokeRemoteMethod(); if (!status) { ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffer: %s", status.GetErrorMessage().c_str()); return -status.error(); } else if (status.get().id() < 0) { ALOGE("BufferHubBuffer::ImportBuffer: Received an invalid id!"); return -EIO; } auto buffer_desc = status.take(); // Stash the buffer id to replace the value in id_. const int new_id = buffer_desc.id(); // Import the buffer. IonBuffer ion_buffer; ALOGD_IF(TRACE, "BufferHubBuffer::ImportBuffer: id=%d.", buffer_desc.id()); if (const int ret = buffer_desc.ImportBuffer(&ion_buffer)) return ret; // Import the metadata. IonBuffer metadata_buffer; if (const int ret = buffer_desc.ImportMetadata(&metadata_buffer)) { ALOGE("Failed to import metadata buffer, error=%d", ret); return ret; } size_t metadata_buf_size = metadata_buffer.width(); if (metadata_buf_size < BufferHubDefs::kMetadataHeaderSize) { ALOGE("BufferHubBuffer::ImportBuffer: metadata buffer too small: %zu", metadata_buf_size); return -ENOMEM; } // If all imports succee, replace the previous buffer and id. buffer_ = std::move(ion_buffer); metadata_buffer_ = std::move(metadata_buffer); metadata_buf_size_ = metadata_buf_size; user_metadata_size_ = metadata_buf_size_ - BufferHubDefs::kMetadataHeaderSize; void* metadata_ptr = nullptr; if (const int ret = metadata_buffer_.Lock(BufferHubDefs::kMetadataUsage, /*x=*/0, /*y=*/0, metadata_buf_size_, /*height=*/1, &metadata_ptr)) { ALOGE("BufferHubBuffer::ImportBuffer: Failed to lock metadata."); return ret; } // Set up shared fences. shared_acquire_fence_ = buffer_desc.take_acquire_fence(); shared_release_fence_ = buffer_desc.take_release_fence(); if (!shared_acquire_fence_ || !shared_release_fence_) { ALOGE("BufferHubBuffer::ImportBuffer: Failed to import shared fences."); return -EIO; } metadata_header_ = reinterpret_cast(metadata_ptr); if (user_metadata_size_) { user_metadata_ptr_ = reinterpret_cast(reinterpret_cast(metadata_ptr) + BufferHubDefs::kMetadataHeaderSize); } else { user_metadata_ptr_ = nullptr; } id_ = new_id; buffer_state_bit_ = buffer_desc.buffer_state_bit(); // Note that here the buffer state is mapped from shared memory as an atomic // object. The std::atomic's constructor will not be called so that the // original value stored in the memory region will be preserved. buffer_state_ = &metadata_header_->buffer_state; ALOGD_IF(TRACE, "BufferHubBuffer::ImportBuffer: id=%d, buffer_state=%" PRIx64 ".", id(), buffer_state_->load()); fence_state_ = &metadata_header_->fence_state; ALOGD_IF(TRACE, "BufferHubBuffer::ImportBuffer: id=%d, fence_state=%" PRIx64 ".", id(), fence_state_->load()); return 0; } inline int BufferHubBuffer::CheckMetadata(size_t user_metadata_size) const { if (user_metadata_size && !user_metadata_ptr_) { ALOGE("BufferHubBuffer::CheckMetadata: doesn't support custom metadata."); return -EINVAL; } if (user_metadata_size > user_metadata_size_) { ALOGE("BufferHubBuffer::CheckMetadata: too big: %zu, maximum: %zu.", user_metadata_size, user_metadata_size_); return -E2BIG; } return 0; } int BufferHubBuffer::UpdateSharedFence(const LocalHandle& new_fence, const LocalHandle& shared_fence) { if (pending_fence_fd_.Get() != new_fence.Get()) { // First, replace the old fd if there was already one. Skipping if the new // one is the same as the old. if (pending_fence_fd_.IsValid()) { const int ret = epoll_ctl(shared_fence.Get(), EPOLL_CTL_DEL, pending_fence_fd_.Get(), nullptr); ALOGW_IF(ret, "BufferHubBuffer::UpdateSharedFence: failed to remove old fence " "fd from epoll set, error: %s.", strerror(errno)); } if (new_fence.IsValid()) { // If ready fence is valid, we put that into the epoll set. epoll_event event; event.events = EPOLLIN; event.data.u64 = buffer_state_bit(); pending_fence_fd_ = new_fence.Duplicate(); if (epoll_ctl(shared_fence.Get(), EPOLL_CTL_ADD, pending_fence_fd_.Get(), &event) < 0) { const int error = errno; ALOGE( "BufferHubBuffer::UpdateSharedFence: failed to add new fence fd " "into epoll set, error: %s.", strerror(error)); return -error; } // Set bit in fence state to indicate that there is a fence from this // producer or consumer. fence_state_->fetch_or(buffer_state_bit()); } else { // Unset bit in fence state to indicate that there is no fence, so that // when consumer to acquire or producer to acquire, it knows no need to // check fence for this buffer. fence_state_->fetch_and(~buffer_state_bit()); } } 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) { return buffer_.Lock(usage, x, y, width, height, address); } int BufferHubBuffer::Unlock() { return buffer_.Unlock(); } int BufferHubBuffer::GetBlobReadWritePointer(size_t size, void** addr) { int width = static_cast(size); int height = 1; int ret = Lock(usage(), 0, 0, width, height, addr); if (ret == 0) Unlock(); return ret; } int BufferHubBuffer::GetBlobReadOnlyPointer(size_t size, void** addr) { return GetBlobReadWritePointer(size, addr); } void BufferHubBuffer::GetBlobFds(int* fds, size_t* fds_count, size_t max_fds_count) const { size_t numFds = static_cast(native_handle()->numFds); *fds_count = std::min(max_fds_count, numFds); std::copy(native_handle()->data, native_handle()->data + *fds_count, fds); } 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::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::Import( Status status) { return Import(status ? status.take() : LocalChannelHandle{nullptr, -status.error()}); } int BufferConsumer::LocalAcquire(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence) { if (!out_meta) return -EINVAL; // Only check producer bit and this consumer buffer's particular consumer bit. // The buffer is can be acquired iff: 1) producer bit is set; 2) consumer bit // is not set. uint64_t buffer_state = buffer_state_->load(); if (!BufferHubDefs::IsBufferPosted(buffer_state, buffer_state_bit())) { ALOGE("BufferConsumer::LocalAcquire: not posted, id=%d state=%" PRIx64 " buffer_state_bit=%" PRIx64 ".", id(), buffer_state, buffer_state_bit()); return -EBUSY; } // Copy the canonical metadata. void* metadata_ptr = reinterpret_cast(&metadata_header_->metadata); memcpy(out_meta, metadata_ptr, sizeof(DvrNativeBufferMetadata)); // Fill in the user_metadata_ptr in address space of the local process. if (out_meta->user_metadata_size) { out_meta->user_metadata_ptr = reinterpret_cast(user_metadata_ptr_); } else { out_meta->user_metadata_ptr = 0; } uint64_t fence_state = fence_state_->load(); // If there is an acquire fence from producer, we need to return it. if (fence_state & BufferHubDefs::kProducerStateBit) { *out_fence = shared_acquire_fence_.Duplicate(); } // Set the consumer bit unique to this consumer. BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, buffer_state_bit()); return 0; } int BufferConsumer::Acquire(LocalHandle* ready_fence) { return Acquire(ready_fence, nullptr, 0); } int BufferConsumer::Acquire(LocalHandle* ready_fence, void* meta, size_t user_metadata_size) { ATRACE_NAME("BufferConsumer::Acquire"); if (const int error = CheckMetadata(user_metadata_size)) return error; DvrNativeBufferMetadata canonical_meta; if (const int error = LocalAcquire(&canonical_meta, ready_fence)) return error; if (meta && user_metadata_size) { void* metadata_src = reinterpret_cast(canonical_meta.user_metadata_ptr); if (metadata_src) { memcpy(meta, metadata_src, user_metadata_size); } else { ALOGW("BufferConsumer::Acquire: no user-defined metadata."); } } auto status = InvokeRemoteMethod(); if (!status) return -status.error(); return 0; } int BufferConsumer::AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence) { ATRACE_NAME("BufferConsumer::AcquireAsync"); if (const int error = LocalAcquire(out_meta, out_fence)) return error; auto status = SendImpulse(BufferHubRPC::ConsumerAcquire::Opcode); if (!status) return -status.error(); return 0; } int BufferConsumer::LocalRelease(const DvrNativeBufferMetadata* meta, const LocalHandle& release_fence) { if (const int error = CheckMetadata(meta->user_metadata_size)) return error; // Check invalid state transition. uint64_t buffer_state = buffer_state_->load(); if (!BufferHubDefs::IsBufferAcquired(buffer_state)) { ALOGE("BufferConsumer::LocalRelease: not acquired id=%d state=%" PRIx64 ".", id(), buffer_state); return -EBUSY; } // On release, only the user requested metadata is copied back into the shared // memory for metadata. Since there are multiple consumers, it doesn't make // sense to send the canonical metadata back to the producer. However, one of // the consumer can still choose to write up to user_metadata_size bytes of // data into user_metadata_ptr. if (meta->user_metadata_ptr && meta->user_metadata_size) { void* metadata_src = reinterpret_cast(meta->user_metadata_ptr); memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size); } // Send out the release fence through the shared epoll fd. Note that during // releasing the producer is not expected to be polling on the fence. if (const int error = UpdateSharedFence(release_fence, shared_release_fence_)) return error; // For release operation, the client don't need to change the state as it's // bufferhubd's job to flip the produer bit once all consumers are released. return 0; } int BufferConsumer::Release(const LocalHandle& release_fence) { ATRACE_NAME("BufferConsumer::Release"); DvrNativeBufferMetadata meta; if (const int error = LocalRelease(&meta, release_fence)) return error; return ReturnStatusOrError(InvokeRemoteMethod( BorrowedFence(release_fence.Borrow()))); } int BufferConsumer::ReleaseAsync() { DvrNativeBufferMetadata meta; return ReleaseAsync(&meta, LocalHandle()); } int BufferConsumer::ReleaseAsync(const DvrNativeBufferMetadata* meta, const LocalHandle& release_fence) { ATRACE_NAME("BufferConsumer::ReleaseAsync"); if (const int error = LocalRelease(meta, release_fence)) return error; return ReturnStatusOrError( SendImpulse(BufferHubRPC::ConsumerRelease::Opcode)); } int BufferConsumer::Discard() { return Release(LocalHandle()); } int BufferConsumer::SetIgnore(bool ignore) { return ReturnStatusOrError( InvokeRemoteMethod(ignore)); } BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format, uint64_t usage, size_t user_metadata_size) : BASE(BufferHubRPC::kClientPath) { ATRACE_NAME("BufferProducer::BufferProducer"); ALOGD_IF(TRACE, "BufferProducer::BufferProducer: fd=%d width=%u height=%u format=%u " "usage=%" PRIx64 " user_metadata_size=%zu", event_fd(), width, height, format, usage, user_metadata_size); auto status = InvokeRemoteMethod( width, height, format, usage, user_metadata_size); 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(uint64_t usage, size_t size) : BASE(BufferHubRPC::kClientPath) { ATRACE_NAME("BufferProducer::BufferProducer"); ALOGD_IF(TRACE, "BufferProducer::BufferProducer: usage=%" PRIx64 " size=%zu", usage, size); const int width = static_cast(size); const int height = 1; const int format = HAL_PIXEL_FORMAT_BLOB; const size_t user_metadata_size = 0; auto status = InvokeRemoteMethod( width, height, format, usage, user_metadata_size); 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(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::LocalPost(const DvrNativeBufferMetadata* meta, const LocalHandle& ready_fence) { if (const int error = CheckMetadata(meta->user_metadata_size)) return error; // Check invalid state transition. uint64_t buffer_state = buffer_state_->load(); if (!BufferHubDefs::IsBufferGained(buffer_state)) { ALOGE("BufferProducer::LocalPost: not gained, id=%d state=%" PRIx64 ".", id(), buffer_state); return -EBUSY; } // Copy the canonical metadata. void* metadata_ptr = reinterpret_cast(&metadata_header_->metadata); memcpy(metadata_ptr, meta, sizeof(DvrNativeBufferMetadata)); // Copy extra user requested metadata. if (meta->user_metadata_ptr && meta->user_metadata_size) { void* metadata_src = reinterpret_cast(meta->user_metadata_ptr); memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size); } // Send out the acquire fence through the shared epoll fd. Note that during // posting no consumer is not expected to be polling on the fence. if (const int error = UpdateSharedFence(ready_fence, shared_acquire_fence_)) return error; // Set the producer bit atomically to transit into posted state. BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, BufferHubDefs::kProducerStateBit); return 0; } int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta, size_t user_metadata_size) { ATRACE_NAME("BufferProducer::Post"); // Populate cononical metadata for posting. DvrNativeBufferMetadata canonical_meta; canonical_meta.user_metadata_ptr = reinterpret_cast(meta); canonical_meta.user_metadata_size = user_metadata_size; if (const int error = LocalPost(&canonical_meta, ready_fence)) return error; return ReturnStatusOrError(InvokeRemoteMethod( BorrowedFence(ready_fence.Borrow()))); } int BufferProducer::PostAsync(const DvrNativeBufferMetadata* meta, const LocalHandle& ready_fence) { ATRACE_NAME("BufferProducer::PostAsync"); if (const int error = LocalPost(meta, ready_fence)) return error; return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerPost::Opcode)); } int BufferProducer::LocalGain(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence) { uint64_t buffer_state = buffer_state_->load(); ALOGD_IF(TRACE, "BufferProducer::LocalGain: buffer=%d, state=%" PRIx64 ".", id(), buffer_state); if (!out_meta) return -EINVAL; if (!BufferHubDefs::IsBufferReleased(buffer_state)) { if (BufferHubDefs::IsBufferGained(buffer_state)) { // We don't want to log error when gaining a newly allocated // buffer. ALOGI("BufferProducer::LocalGain: already gained id=%d.", id()); return -EALREADY; } ALOGE("BufferProducer::LocalGain: not released id=%d state=%" PRIx64 ".", id(), buffer_state); return -EBUSY; } // Canonical metadata is undefined on Gain. Except for user_metadata and // release_fence_mask. Fill in the user_metadata_ptr in address space of the // local process. if (metadata_header_->metadata.user_metadata_size && user_metadata_ptr_) { out_meta->user_metadata_size = metadata_header_->metadata.user_metadata_size; out_meta->user_metadata_ptr = reinterpret_cast(user_metadata_ptr_); } else { out_meta->user_metadata_size = 0; out_meta->user_metadata_ptr = 0; } uint64_t fence_state = fence_state_->load(); // If there is an release fence from consumer, we need to return it. if (fence_state & BufferHubDefs::kConsumerStateMask) { *out_fence = shared_release_fence_.Duplicate(); out_meta->release_fence_mask = fence_state & BufferHubDefs::kConsumerStateMask; } // Clear out all bits and the buffer is now back to gained state. buffer_state_->store(0ULL); return 0; } int BufferProducer::Gain(LocalHandle* release_fence) { ATRACE_NAME("BufferProducer::Gain"); DvrNativeBufferMetadata meta; if (const int error = LocalGain(&meta, release_fence)) return error; auto status = InvokeRemoteMethod(); if (!status) return -status.error(); return 0; } int BufferProducer::GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* release_fence) { ATRACE_NAME("BufferProducer::GainAsync"); if (const int error = LocalGain(out_meta, release_fence)) return error; return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode)); } int BufferProducer::GainAsync() { DvrNativeBufferMetadata meta; LocalHandle fence; return GainAsync(&meta, &fence); } std::unique_ptr BufferProducer::Import( LocalChannelHandle channel) { ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value()); return BufferProducer::Create(std::move(channel)); } std::unique_ptr BufferProducer::Import( Status status) { return Import(status ? status.take() : LocalChannelHandle{nullptr, -status.error()}); } Status BufferProducer::Detach() { uint64_t buffer_state = buffer_state_->load(); if (!BufferHubDefs::IsBufferGained(buffer_state)) { // Can only detach a BufferProducer when it's in gained state. ALOGW("BufferProducer::Detach: The buffer (id=%d, state=0x%" PRIx64 ") is not in gained state.", id(), buffer_state); return {}; } Status status = InvokeRemoteMethod(); ALOGE_IF(!status, "BufferProducer::Detach: Failed to detach buffer (id=%d): %s.", id(), status.GetErrorMessage().c_str()); return status; } } // namespace dvr } // namespace android