| #include "producer_channel.h" |
| |
| #include <log/log.h> |
| #include <sync/sync.h> |
| #include <sys/poll.h> |
| #include <utils/Trace.h> |
| |
| #include <algorithm> |
| #include <atomic> |
| #include <thread> |
| |
| #include <private/dvr/bufferhub_rpc.h> |
| #include "consumer_channel.h" |
| |
| using android::pdx::BorrowedHandle; |
| using android::pdx::ErrorStatus; |
| using android::pdx::Message; |
| using android::pdx::RemoteChannelHandle; |
| using android::pdx::Status; |
| using android::pdx::rpc::BufferWrapper; |
| using android::pdx::rpc::DispatchRemoteMethod; |
| using android::pdx::rpc::WrapBuffer; |
| |
| namespace android { |
| namespace dvr { |
| |
| ProducerChannel::ProducerChannel(BufferHubService* service, int channel_id, |
| uint32_t width, uint32_t height, |
| uint32_t layer_count, uint32_t format, |
| uint64_t usage, size_t meta_size_bytes, |
| int* error) |
| : BufferHubChannel(service, channel_id, channel_id, kProducerType), |
| pending_consumers_(0), |
| producer_owns_(true), |
| meta_size_bytes_(meta_size_bytes), |
| meta_(meta_size_bytes ? new uint8_t[meta_size_bytes] : nullptr) { |
| const int ret = buffer_.Alloc(width, height, layer_count, format, usage); |
| if (ret < 0) { |
| ALOGE("ProducerChannel::ProducerChannel: Failed to allocate buffer: %s", |
| strerror(-ret)); |
| *error = ret; |
| return; |
| } |
| |
| // Success. |
| *error = 0; |
| } |
| |
| Status<std::shared_ptr<ProducerChannel>> ProducerChannel::Create( |
| BufferHubService* service, int channel_id, uint32_t width, uint32_t height, |
| uint32_t layer_count, uint32_t format, uint64_t usage, |
| size_t meta_size_bytes) { |
| int error; |
| std::shared_ptr<ProducerChannel> producer( |
| new ProducerChannel(service, channel_id, width, height, layer_count, |
| format, usage, meta_size_bytes, &error)); |
| if (error < 0) |
| return ErrorStatus(-error); |
| else |
| return {std::move(producer)}; |
| } |
| |
| ProducerChannel::~ProducerChannel() { |
| ALOGD_IF(TRACE, |
| "ProducerChannel::~ProducerChannel: channel_id=%d buffer_id=%d", |
| channel_id(), buffer_id()); |
| for (auto consumer : consumer_channels_) |
| consumer->OnProducerClosed(); |
| } |
| |
| BufferHubChannel::BufferInfo ProducerChannel::GetBufferInfo() const { |
| return BufferInfo(buffer_id(), consumer_channels_.size(), buffer_.width(), |
| buffer_.height(), buffer_.layer_count(), buffer_.format(), |
| buffer_.usage(), name_); |
| } |
| |
| void ProducerChannel::HandleImpulse(Message& message) { |
| ATRACE_NAME("ProducerChannel::HandleImpulse"); |
| switch (message.GetOp()) { |
| case BufferHubRPC::ProducerGain::Opcode: |
| OnProducerGain(message); |
| break; |
| } |
| } |
| |
| bool ProducerChannel::HandleMessage(Message& message) { |
| ATRACE_NAME("ProducerChannel::HandleMessage"); |
| switch (message.GetOp()) { |
| case BufferHubRPC::GetBuffer::Opcode: |
| DispatchRemoteMethod<BufferHubRPC::GetBuffer>( |
| *this, &ProducerChannel::OnGetBuffer, message); |
| return true; |
| |
| case BufferHubRPC::NewConsumer::Opcode: |
| DispatchRemoteMethod<BufferHubRPC::NewConsumer>( |
| *this, &ProducerChannel::OnNewConsumer, message); |
| return true; |
| |
| case BufferHubRPC::ProducerPost::Opcode: |
| DispatchRemoteMethod<BufferHubRPC::ProducerPost>( |
| *this, &ProducerChannel::OnProducerPost, message); |
| return true; |
| |
| case BufferHubRPC::ProducerGain::Opcode: |
| DispatchRemoteMethod<BufferHubRPC::ProducerGain>( |
| *this, &ProducerChannel::OnProducerGain, message); |
| return true; |
| |
| case BufferHubRPC::ProducerMakePersistent::Opcode: |
| DispatchRemoteMethod<BufferHubRPC::ProducerMakePersistent>( |
| *this, &ProducerChannel::OnProducerMakePersistent, message); |
| return true; |
| |
| case BufferHubRPC::ProducerRemovePersistence::Opcode: |
| DispatchRemoteMethod<BufferHubRPC::ProducerRemovePersistence>( |
| *this, &ProducerChannel::OnRemovePersistence, message); |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| Status<NativeBufferHandle<BorrowedHandle>> ProducerChannel::OnGetBuffer( |
| Message& message) { |
| ATRACE_NAME("ProducerChannel::OnGetBuffer"); |
| ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffer: buffer=%d", buffer_id()); |
| return {NativeBufferHandle<BorrowedHandle>(buffer_, buffer_id())}; |
| } |
| |
| Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(Message& message) { |
| ATRACE_NAME("ProducerChannel::CreateConsumer"); |
| ALOGD_IF(TRACE, "ProducerChannel::CreateConsumer: buffer_id=%d", buffer_id()); |
| |
| int channel_id; |
| auto status = message.PushChannel(0, nullptr, &channel_id); |
| if (!status) { |
| ALOGE( |
| "ProducerChannel::CreateConsumer: Failed to push consumer channel: %s", |
| status.GetErrorMessage().c_str()); |
| return ErrorStatus(ENOMEM); |
| } |
| |
| auto consumer = std::make_shared<ConsumerChannel>( |
| service(), buffer_id(), channel_id, shared_from_this()); |
| const auto channel_status = service()->SetChannel(channel_id, consumer); |
| if (!channel_status) { |
| ALOGE( |
| "ProducerChannel::CreateConsumer: failed to set new consumer channel: " |
| "%s", |
| channel_status.GetErrorMessage().c_str()); |
| return ErrorStatus(ENOMEM); |
| } |
| |
| if (!producer_owns_) { |
| // Signal the new consumer when adding it to a posted producer. |
| if (consumer->OnProducerPosted()) |
| pending_consumers_++; |
| } |
| |
| return {status.take()}; |
| } |
| |
| Status<RemoteChannelHandle> ProducerChannel::OnNewConsumer(Message& message) { |
| ATRACE_NAME("ProducerChannel::OnNewConsumer"); |
| ALOGD_IF(TRACE, "ProducerChannel::OnNewConsumer: buffer_id=%d", buffer_id()); |
| return CreateConsumer(message); |
| } |
| |
| Status<void> ProducerChannel::OnProducerPost( |
| Message&, LocalFence acquire_fence, |
| BufferWrapper<std::vector<std::uint8_t>> metadata) { |
| ATRACE_NAME("ProducerChannel::OnProducerPost"); |
| ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: buffer_id=%d", buffer_id()); |
| if (!producer_owns_) { |
| ALOGE("ProducerChannel::OnProducerPost: Not in gained state!"); |
| return ErrorStatus(EBUSY); |
| } |
| |
| if (meta_size_bytes_ != metadata.size()) { |
| ALOGD_IF(TRACE, |
| "ProducerChannel::OnProducerPost: Expected meta_size_bytes=%zu " |
| "got size=%zu", |
| meta_size_bytes_, metadata.size()); |
| return ErrorStatus(EINVAL); |
| } |
| |
| std::copy(metadata.begin(), metadata.end(), meta_.get()); |
| post_fence_ = std::move(acquire_fence); |
| producer_owns_ = false; |
| |
| // Signal any interested consumers. If there are none, automatically release |
| // the buffer. |
| pending_consumers_ = 0; |
| for (auto consumer : consumer_channels_) { |
| if (consumer->OnProducerPosted()) |
| pending_consumers_++; |
| } |
| if (pending_consumers_ == 0) |
| SignalAvailable(); |
| ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: %d pending consumers", |
| pending_consumers_); |
| |
| return {}; |
| } |
| |
| Status<LocalFence> ProducerChannel::OnProducerGain(Message& message) { |
| ATRACE_NAME("ProducerChannel::OnGain"); |
| ALOGD_IF(TRACE, "ProducerChannel::OnGain: buffer_id=%d", buffer_id()); |
| if (producer_owns_) { |
| ALOGE("ProducerChanneL::OnGain: Already in gained state: channel=%d", |
| channel_id()); |
| return ErrorStatus(EALREADY); |
| } |
| |
| // There are still pending consumers, return busy. |
| if (pending_consumers_ > 0) |
| return ErrorStatus(EBUSY); |
| |
| ClearAvailable(); |
| producer_owns_ = true; |
| post_fence_.close(); |
| return {std::move(returned_fence_)}; |
| } |
| |
| Status<std::pair<BorrowedFence, BufferWrapper<std::uint8_t*>>> |
| ProducerChannel::OnConsumerAcquire(Message& message, |
| std::size_t metadata_size) { |
| ATRACE_NAME("ProducerChannel::OnConsumerAcquire"); |
| ALOGD_IF(TRACE, "ProducerChannel::OnConsumerAcquire: buffer_id=%d", |
| buffer_id()); |
| if (producer_owns_) { |
| ALOGE("ProducerChannel::OnConsumerAcquire: Not in posted state!"); |
| return ErrorStatus(EBUSY); |
| } |
| |
| // Return a borrowed fd to avoid unnecessary duplication of the underlying fd. |
| // Serialization just needs to read the handle. |
| if (metadata_size == 0) |
| return {std::make_pair(post_fence_.borrow(), |
| WrapBuffer<std::uint8_t>(nullptr, 0))}; |
| else |
| return {std::make_pair(post_fence_.borrow(), |
| WrapBuffer(meta_.get(), meta_size_bytes_))}; |
| } |
| |
| Status<void> ProducerChannel::OnConsumerRelease(Message&, |
| LocalFence release_fence) { |
| ATRACE_NAME("ProducerChannel::OnConsumerRelease"); |
| ALOGD_IF(TRACE, "ProducerChannel::OnConsumerRelease: buffer_id=%d", |
| buffer_id()); |
| if (producer_owns_) { |
| ALOGE("ProducerChannel::OnConsumerRelease: Not in acquired state!"); |
| return ErrorStatus(EBUSY); |
| } |
| |
| // Attempt to merge the fences if necessary. |
| if (release_fence) { |
| if (returned_fence_) { |
| LocalFence merged_fence(sync_merge("bufferhub_merged", |
| returned_fence_.get_fd(), |
| release_fence.get_fd())); |
| const int error = errno; |
| if (!merged_fence) { |
| ALOGE("ProducerChannel::OnConsumerRelease: Failed to merge fences: %s", |
| strerror(error)); |
| return ErrorStatus(error); |
| } |
| returned_fence_ = std::move(merged_fence); |
| } else { |
| returned_fence_ = std::move(release_fence); |
| } |
| } |
| |
| OnConsumerIgnored(); |
| return {}; |
| } |
| |
| void ProducerChannel::OnConsumerIgnored() { |
| if (!--pending_consumers_) |
| SignalAvailable(); |
| ALOGD_IF(TRACE, |
| "ProducerChannel::OnConsumerIgnored: buffer_id=%d %d consumers left", |
| buffer_id(), pending_consumers_); |
| } |
| |
| Status<void> ProducerChannel::OnProducerMakePersistent(Message& message, |
| const std::string& name, |
| int user_id, |
| int group_id) { |
| ATRACE_NAME("ProducerChannel::OnProducerMakePersistent"); |
| ALOGD_IF(TRACE, |
| "ProducerChannel::OnProducerMakePersistent: buffer_id=%d name=%s " |
| "user_id=%d group_id=%d", |
| buffer_id(), name.c_str(), user_id, group_id); |
| |
| if (name.empty() || (user_id < 0 && user_id != kNoCheckId) || |
| (group_id < 0 && group_id != kNoCheckId)) { |
| return ErrorStatus(EINVAL); |
| } |
| |
| // Try to add this buffer with the requested name. |
| if (service()->AddNamedBuffer(name, std::static_pointer_cast<ProducerChannel>( |
| shared_from_this()))) { |
| // If successful, set the requested permissions. |
| |
| // A value of zero indicates that the ids from the sending process should be |
| // used. |
| if (user_id == kUseCallerId) |
| user_id = message.GetEffectiveUserId(); |
| if (group_id == kUseCallerId) |
| group_id = message.GetEffectiveGroupId(); |
| |
| owner_user_id_ = user_id; |
| owner_group_id_ = group_id; |
| name_ = name; |
| return {}; |
| } else { |
| // Otherwise a buffer with that name already exists. |
| return ErrorStatus(EALREADY); |
| } |
| } |
| |
| Status<void> ProducerChannel::OnRemovePersistence(Message&) { |
| if (service()->RemoveNamedBuffer(*this)) |
| return {}; |
| else |
| return ErrorStatus(ENOENT); |
| } |
| |
| void ProducerChannel::AddConsumer(ConsumerChannel* channel) { |
| consumer_channels_.push_back(channel); |
| } |
| |
| void ProducerChannel::RemoveConsumer(ConsumerChannel* channel) { |
| consumer_channels_.erase( |
| std::find(consumer_channels_.begin(), consumer_channels_.end(), channel)); |
| } |
| |
| // Returns true if either the user or group ids match the owning ids or both |
| // owning ids are not set, in which case access control does not apply. |
| bool ProducerChannel::CheckAccess(int euid, int egid) { |
| const bool no_check = |
| owner_user_id_ == kNoCheckId && owner_group_id_ == kNoCheckId; |
| const bool euid_check = euid == owner_user_id_ || euid == kRootId; |
| const bool egid_check = egid == owner_group_id_ || egid == kRootId; |
| return no_check || euid_check || egid_check; |
| } |
| |
| // Returns true if the given parameters match the underlying buffer parameters. |
| bool ProducerChannel::CheckParameters(uint32_t width, uint32_t height, |
| uint32_t layer_count, uint32_t format, |
| uint64_t usage, size_t meta_size_bytes) { |
| return meta_size_bytes == meta_size_bytes_ && buffer_.width() == width && |
| buffer_.height() == height && buffer_.layer_count() == layer_count && |
| buffer_.format() == format && buffer_.usage() == usage; |
| } |
| |
| } // namespace dvr |
| } // namespace android |