| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromeos/components/sensors/fake_sensor_device.h" |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #include "base/containers/flat_map.h" |
| #include "base/functional/bind.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/task/sequenced_task_runner.h" |
| |
| namespace chromeos { |
| namespace sensors { |
| |
| FakeSensorDevice::ChannelData::ChannelData() = default; |
| FakeSensorDevice::ChannelData::ChannelData( |
| const FakeSensorDevice::ChannelData&) = default; |
| FakeSensorDevice::ChannelData& FakeSensorDevice::ChannelData::operator=( |
| const FakeSensorDevice::ChannelData&) = default; |
| FakeSensorDevice::ChannelData::~ChannelData() = default; |
| |
| FakeSensorDevice::FakeSensorDevice(const std::vector<ChannelData>& channels) |
| : channels_(channels) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| for (auto& client : clients_) |
| client.second.channels_enabled.assign(channels_.size(), false); |
| } |
| |
| FakeSensorDevice::~FakeSensorDevice() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| mojo::ReceiverId FakeSensorDevice::AddReceiver( |
| mojo::PendingReceiver<mojom::SensorDevice> pending_receiver) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| auto id = receiver_set_.Add(this, std::move(pending_receiver)); |
| DCHECK(clients_.find(id) == clients_.end()); |
| clients_[id].channels_enabled.assign(channels_.size(), false); |
| |
| return id; |
| } |
| |
| void FakeSensorDevice::RemoveReceiver(mojo::ReceiverId id) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(receiver_set_.HasReceiver(id)); |
| |
| clients_.erase(id); |
| receiver_set_.Remove(id); |
| } |
| |
| void FakeSensorDevice::RemoveReceiverWithReason( |
| mojo::ReceiverId id, |
| mojom::SensorDeviceDisconnectReason reason, |
| const std::string& description) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(receiver_set_.HasReceiver(id)); |
| |
| uint32_t custom_reason_code = base::checked_cast<uint32_t>(reason); |
| |
| auto it = clients_.find(id); |
| if (it != clients_.end()) { |
| it->second.observer.ResetWithReason(custom_reason_code, description); |
| clients_.erase(it); |
| } |
| |
| receiver_set_.RemoveWithReason(id, custom_reason_code, description); |
| } |
| |
| void FakeSensorDevice::ClearReceivers() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| clients_.clear(); |
| receiver_set_.Clear(); |
| } |
| |
| void FakeSensorDevice::ClearReceiversWithReason( |
| mojom::SensorDeviceDisconnectReason reason, |
| const std::string& description) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| uint32_t custom_reason_code = base::checked_cast<uint32_t>(reason); |
| |
| for (auto& client : clients_) |
| client.second.observer.ResetWithReason(custom_reason_code, description); |
| clients_.clear(); |
| |
| receiver_set_.ClearWithReason(custom_reason_code, description); |
| } |
| |
| bool FakeSensorDevice::HasReceivers() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| return !receiver_set_.empty(); |
| } |
| |
| size_t FakeSensorDevice::SizeOfReceivers() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| return receiver_set_.size(); |
| } |
| |
| void FakeSensorDevice::SetAttribute(const std::string& attr_name, |
| const std::string& attr_value) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| attributes_[attr_name] = attr_value; |
| } |
| |
| void FakeSensorDevice::ResetObserverRemote(mojo::ReceiverId id) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| auto it = clients_.find(id); |
| if (it == clients_.end()) |
| return; |
| |
| it->second.observer.reset(); |
| } |
| |
| void FakeSensorDevice::ResetObserverRemoteWithReason( |
| mojo::ReceiverId id, |
| mojom::SensorDeviceDisconnectReason reason, |
| const std::string& description) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| auto it = clients_.find(id); |
| if (it == clients_.end()) |
| return; |
| |
| it->second.observer.ResetWithReason(base::checked_cast<uint32_t>(reason), |
| description); |
| } |
| |
| void FakeSensorDevice::SetChannelsEnabledWithId( |
| mojo::ReceiverId id, |
| const std::vector<int32_t>& iio_chn_indices, |
| bool en) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| auto it = clients_.find(id); |
| CHECK(it != clients_.end()); |
| |
| for (int32_t index : iio_chn_indices) { |
| DCHECK_LT(static_cast<size_t>(index), it->second.channels_enabled.size()); |
| |
| it->second.channels_enabled[index] = en; |
| } |
| |
| SendSampleIfReady(it->second); |
| } |
| |
| void FakeSensorDevice::GetAttributes(const std::vector<std::string>& attr_names, |
| GetAttributesCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| std::vector<std::optional<std::string>> values; |
| values.reserve(attr_names.size()); |
| for (const auto& attr_name : attr_names) { |
| auto it = attributes_.find(attr_name); |
| if (it != attributes_.end()) |
| values.push_back(it->second); |
| else |
| values.push_back(std::nullopt); |
| } |
| |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), std::move(values))); |
| } |
| |
| void FakeSensorDevice::SetFrequency(double frequency, |
| SetFrequencyCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (frequency < 0.0) |
| frequency = 0.0; |
| |
| auto& client = clients_[receiver_set_.current_receiver()]; |
| |
| client.frequency = frequency; |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), std::move(frequency))); |
| |
| SendSampleIfReady(client); |
| } |
| |
| void FakeSensorDevice::StartReadingSamples( |
| mojo::PendingRemote<mojom::SensorDeviceSamplesObserver> observer) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| auto id = receiver_set_.current_receiver(); |
| auto& client = clients_[id]; |
| |
| if (client.observer.is_bound()) { |
| mojo::Remote<mojom::SensorDeviceSamplesObserver> remote( |
| std::move(observer)); |
| remote->OnErrorOccurred(mojom::ObserverErrorType::ALREADY_STARTED); |
| |
| return; |
| } |
| |
| client.observer.Bind(std::move(observer)); |
| client.observer.set_disconnect_handler(base::BindOnce( |
| &FakeSensorDevice::ResetObserverRemote, base::Unretained(this), id)); |
| |
| if (!client.frequency.has_value() || client.frequency.value() <= 0.0) { |
| client.observer->OnErrorOccurred( |
| mojom::ObserverErrorType::FREQUENCY_INVALID); |
| return; |
| } |
| |
| if (std::ranges::none_of(client.channels_enabled, |
| [](bool enabled) { return enabled; })) { |
| client.observer->OnErrorOccurred( |
| mojom::ObserverErrorType::NO_ENABLED_CHANNELS); |
| return; |
| } |
| |
| SendSampleIfReady(client); |
| } |
| |
| void FakeSensorDevice::StopReadingSamples() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| clients_[receiver_set_.current_receiver()].observer.reset(); |
| } |
| |
| void FakeSensorDevice::GetAllChannelIds(GetAllChannelIdsCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| std::vector<std::string> channel_ids; |
| for (const ChannelData& channel : channels_) |
| channel_ids.push_back(channel.id); |
| |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), std::move(channel_ids))); |
| } |
| |
| void FakeSensorDevice::SetChannelsEnabled( |
| const std::vector<int32_t>& iio_chn_indices, |
| bool en, |
| SetChannelsEnabledCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| auto& client = clients_[receiver_set_.current_receiver()]; |
| |
| std::vector<int32_t> failed_indices; |
| for (int32_t index : iio_chn_indices) { |
| if (static_cast<size_t>(index) >= client.channels_enabled.size()) { |
| failed_indices.push_back(index); |
| continue; |
| } |
| |
| client.channels_enabled[index] = en; |
| } |
| |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce(std::move(callback), std::move(failed_indices))); |
| |
| SendSampleIfReady(client); |
| } |
| |
| void FakeSensorDevice::GetChannelsEnabled( |
| const std::vector<int32_t>& iio_chn_indices, |
| GetChannelsEnabledCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| auto& client = clients_[receiver_set_.current_receiver()]; |
| |
| std::vector<bool> enabled; |
| for (int32_t index : iio_chn_indices) { |
| if (static_cast<size_t>(index) >= client.channels_enabled.size()) { |
| enabled.push_back(false); |
| continue; |
| } |
| |
| enabled.push_back(client.channels_enabled[index]); |
| } |
| |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), std::move(enabled))); |
| } |
| |
| void FakeSensorDevice::GetChannelsAttributes( |
| const std::vector<int32_t>& iio_chn_indices, |
| const std::string& attr_name, |
| GetChannelsAttributesCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| std::vector<std::optional<std::string>> attrs; |
| |
| for (const ChannelData& channel : channels_) { |
| auto it = channel.attrs.find(attr_name); |
| if (it == channel.attrs.end()) { |
| attrs.push_back(std::nullopt); |
| continue; |
| } |
| |
| attrs.push_back(it->second); |
| } |
| |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), std::move(attrs))); |
| } |
| |
| FakeSensorDevice::ClientData::ClientData() = default; |
| FakeSensorDevice::ClientData::~ClientData() = default; |
| |
| void FakeSensorDevice::SendSampleIfReady(ClientData& client) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK_EQ(channels_.size(), client.channels_enabled.size()); |
| |
| if (!client.observer.is_bound()) |
| return; |
| |
| if (!client.frequency.has_value() || client.frequency.value() <= 0.0) |
| return; |
| |
| base::flat_map<int32_t, int64_t> sample; |
| for (size_t i = 0; i < channels_.size(); ++i) { |
| if (!client.channels_enabled[i]) |
| continue; |
| |
| sample[i] = channels_[i].sample_data; |
| } |
| |
| if (sample.empty()) |
| return; |
| |
| client.observer->OnSampleUpdated(std::move(sample)); |
| } |
| |
| } // namespace sensors |
| } // namespace chromeos |