| // Copyright 2022 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "iioservice/daemon/events_handler.h" |
| |
| #include <optional> |
| #include <utility> |
| |
| #include <base/containers/contains.h> |
| #include <base/functional/bind.h> |
| |
| #include "iioservice/daemon/common_types.h" |
| #include "iioservice/include/common.h" |
| |
| namespace iioservice { |
| |
| namespace { |
| |
| cros::mojom::IioEventPtr ExtractIioEvent(iio_event_data event) { |
| cros::mojom::IioEvent iio_event; |
| uint64_t mask = event.id; |
| |
| return cros::mojom::IioEvent::New( |
| ConvertChanType( |
| static_cast<iio_chan_type>(IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(mask))), |
| ConvertEventType( |
| static_cast<iio_event_type>(IIO_EVENT_CODE_EXTRACT_TYPE(mask))), |
| ConvertDirection( |
| static_cast<iio_event_direction>(IIO_EVENT_CODE_EXTRACT_DIR(mask))), |
| IIO_EVENT_CODE_EXTRACT_CHAN(mask), event.timestamp); |
| } |
| |
| } // namespace |
| |
| // static |
| void EventsHandler::EventsHandlerDeleter(EventsHandler* handler) { |
| if (handler == nullptr) |
| return; |
| |
| if (!handler->event_task_runner_->BelongsToCurrentThread()) { |
| handler->event_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&EventsHandler::EventsHandlerDeleter, handler)); |
| return; |
| } |
| |
| delete handler; |
| } |
| |
| // static |
| EventsHandler::ScopedEventsHandler EventsHandler::Create( |
| scoped_refptr<base::SequencedTaskRunner> ipc_task_runner, |
| scoped_refptr<base::SingleThreadTaskRunner> event_task_runner, |
| libmems::IioDevice* iio_device) { |
| ScopedEventsHandler handler(nullptr, EventsHandlerDeleter); |
| |
| handler.reset(new EventsHandler(std::move(ipc_task_runner), |
| std::move(event_task_runner), iio_device)); |
| return handler; |
| } |
| |
| EventsHandler::~EventsHandler() { |
| DCHECK(event_task_runner_->RunsTasksInCurrentSequence()); |
| } |
| |
| void EventsHandler::ResetWithReason( |
| cros::mojom::SensorDeviceDisconnectReason reason, |
| std::string description, |
| base::OnceCallback<void()> callback) { |
| event_task_runner_->PostTaskAndReply( |
| FROM_HERE, |
| base::BindOnce(&EventsHandler::ResetWithReasonOnThread, |
| weak_factory_.GetWeakPtr(), reason, description), |
| std::move(callback)); |
| } |
| |
| void EventsHandler::AddClient( |
| const std::vector<int32_t>& iio_event_indices, |
| mojo::PendingRemote<cros::mojom::SensorDeviceEventsObserver> |
| events_observer) { |
| DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence()); |
| DCHECK(!iio_event_indices.empty()); |
| |
| event_task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&EventsHandler::AddClientOnThread, |
| weak_factory_.GetWeakPtr(), iio_event_indices, |
| std::move(events_observer))); |
| } |
| |
| EventsHandler::EventsHandler( |
| scoped_refptr<base::SequencedTaskRunner> ipc_task_runner, |
| scoped_refptr<base::SingleThreadTaskRunner> event_task_runner, |
| libmems::IioDevice* iio_device) |
| : ipc_task_runner_(std::move(ipc_task_runner)), |
| event_task_runner_(std::move(event_task_runner)), |
| iio_device_(iio_device) { |
| events_observers_.set_disconnect_handler(base::BindRepeating( |
| &EventsHandler::OnEventsObserverDisconnect, weak_factory_.GetWeakPtr())); |
| } |
| |
| void EventsHandler::ResetWithReasonOnThread( |
| cros::mojom::SensorDeviceDisconnectReason reason, std::string description) { |
| DCHECK(event_task_runner_->RunsTasksInCurrentSequence()); |
| |
| // TODO(crbug/1414799): Reset with reason when mojo::RemoteSet supports it. |
| events_observers_.Clear(); |
| enabled_indices_.clear(); |
| enabled_indices_count_.clear(); |
| |
| StopEventWatcherOnThread(); |
| } |
| |
| void EventsHandler::AddClientOnThread( |
| const std::vector<int32_t>& iio_event_indices, |
| mojo::PendingRemote<cros::mojom::SensorDeviceEventsObserver> |
| events_observer) { |
| DCHECK(event_task_runner_->RunsTasksInCurrentSequence()); |
| |
| enabled_indices_[events_observers_.Add(std::move(events_observer))] = |
| iio_event_indices; |
| |
| for (int32_t index : iio_event_indices) { |
| if (enabled_indices_count_[index]++ == 0) { |
| auto* event = iio_device_->GetEvent(index); |
| if (!event) { |
| LOGF(ERROR) << "Invalid event index: " << index; |
| continue; |
| } |
| |
| if (!event->SetEnabledAndCheck(true)) |
| LOGF(ERROR) << "Failed to enable event: " << index; |
| } |
| } |
| |
| if (!watcher_.get()) |
| SetEventWatcherOnThread(); |
| } |
| |
| void EventsHandler::OnEventsObserverDisconnect(mojo::RemoteSetElementId id) { |
| DCHECK(event_task_runner_->RunsTasksInCurrentSequence()); |
| |
| LOGF(ERROR) << "EventsObserver disconnected. RemoteSetElementId: " << id; |
| |
| auto it = enabled_indices_.find(id); |
| if (it == enabled_indices_.end()) |
| return; |
| |
| for (int32_t index : it->second) { |
| if (--enabled_indices_count_[index] == 0) { |
| auto* event = iio_device_->GetEvent(index); |
| if (!event) { |
| LOGF(ERROR) << "Invalid event index: " << index; |
| continue; |
| } |
| |
| if (!event->SetEnabledAndCheck(false)) |
| LOGF(ERROR) << "Failed to disable event: " << index; |
| } |
| } |
| |
| enabled_indices_.erase(it); |
| if (enabled_indices_.empty()) |
| StopEventWatcherOnThread(); |
| } |
| |
| void EventsHandler::SetEventWatcherOnThread() { |
| DCHECK(event_task_runner_->RunsTasksInCurrentSequence()); |
| DCHECK(!watcher_.get()); |
| |
| auto fd = iio_device_->GetEventFd(); |
| if (!fd.has_value()) { |
| LOGF(ERROR) << "Failed to get fd"; |
| for (auto& observer : events_observers_) |
| observer->OnErrorOccurred(cros::mojom::ObserverErrorType::GET_FD_FAILED); |
| |
| return; |
| } |
| |
| watcher_ = base::FileDescriptorWatcher::WatchReadable( |
| fd.value(), |
| base::BindRepeating(&EventsHandler::OnEventAvailableWithoutBlocking, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void EventsHandler::StopEventWatcherOnThread() { |
| DCHECK(event_task_runner_->RunsTasksInCurrentSequence()); |
| |
| watcher_.reset(); |
| } |
| |
| void EventsHandler::OnEventAvailableWithoutBlocking() { |
| DCHECK(event_task_runner_->RunsTasksInCurrentSequence()); |
| |
| auto event = iio_device_->ReadEvent(); |
| if (!event) { |
| for (auto& observer : events_observers_) |
| observer->OnErrorOccurred(cros::mojom::ObserverErrorType::READ_FAILED); |
| |
| return; |
| } |
| |
| cros::mojom::IioEventPtr iio_event = ExtractIioEvent(event.value()); |
| std::optional<int32_t> chn_index; |
| for (int32_t i = 0, size = iio_device_->GetAllEvents().size(); i < size; |
| ++i) { |
| if (iio_device_->GetEvent(i)->MatchMask(event.value().id)) { |
| chn_index = i; |
| break; |
| } |
| } |
| if (!chn_index.has_value()) { |
| LOGF(ERROR) << "No existing events match the mask: " << event.value().id; |
| return; |
| } |
| |
| for (auto& [id, indices] : enabled_indices_) { |
| if (!base::Contains(indices, chn_index.value())) |
| continue; |
| |
| events_observers_.Get(id)->OnEventUpdated(iio_event.Clone()); |
| } |
| } |
| |
| } // namespace iioservice |