// 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 <utility>
#include "base/containers/flat_map.h"
#include "base/functional/bind.h"
#include "base/not_fatal_until.h"
#include "base/numerics/safe_conversions.h"
#include "base/ranges/algorithm.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(), base::NotFatalUntil::M130);
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 (base::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