blob: 61c4b9031243af92f3782a79c0bfbd9a5fbe44f2 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "power_manager/powerd/system/ambient_light_sensor_delegate_mojo.h"
#include <fcntl.h>
#include <algorithm>
#include <cmath>
#include <map>
#include <utility>
#include <base/bind.h>
#include <base/check.h>
#include <base/check_op.h>
#include <base/containers/flat_map.h>
#include <base/logging.h>
#include "base/strings/string_number_conversions.h"
#include <base/strings/stringprintf.h>
namespace power_manager {
namespace system {
namespace {
constexpr double kFrequencyInHz = 1.0;
const struct ColorChannelInfo* ChannelIdToColorChannelInfo(
const std::string& id) {
for (const ColorChannelInfo& channel : kColorChannelConfig) {
if (id == AmbientLightSensorDelegateMojo::GetChannelIlluminanceColorId(
channel.rgb_name)) {
return &channel;
}
}
return nullptr;
}
} // namespace
// static
std::string AmbientLightSensorDelegateMojo::GetChannelIlluminanceColorId(
const char* rgb_name) {
return base::StringPrintf("%s_%s", cros::mojom::kLightChannel, rgb_name);
}
// static
std::unique_ptr<AmbientLightSensorDelegateMojo>
AmbientLightSensorDelegateMojo::Create(
int iio_device_id,
mojo::Remote<cros::mojom::SensorDevice> remote,
bool enable_color_support,
base::OnceClosure init_closure) {
if (!remote.is_bound())
return nullptr;
std::unique_ptr<AmbientLightSensorDelegateMojo> sensor_mojo(
new AmbientLightSensorDelegateMojo(iio_device_id, std::move(remote),
enable_color_support,
std::move(init_closure)));
return sensor_mojo;
}
AmbientLightSensorDelegateMojo::~AmbientLightSensorDelegateMojo() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
bool AmbientLightSensorDelegateMojo::IsColorSensor() const {
return color_channels_enabled_;
}
base::FilePath AmbientLightSensorDelegateMojo::GetIlluminancePath() const {
return base::FilePath();
}
void AmbientLightSensorDelegateMojo::OnSampleUpdated(
const base::flat_map<int32_t, int64_t>& sample) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(illuminance_index_.has_value());
DCHECK_LT(illuminance_index_.value(), iio_channel_ids_.size());
if (!set_lux_callback_)
return;
auto it = sample.find(illuminance_index_.value());
base::Optional<int> lux_value, color_temperature;
if (it == sample.end()) {
VLOG(2) << "No channel " << cros::mojom::kLightChannel
<< " found in the sample.";
OnErrorOccurred(cros::mojom::ObserverErrorType::READ_FAILED);
} else if (it->second < 0) {
VLOG(2) << "Invalid " << cros::mojom::kLightChannel << ": " << it->second;
OnErrorOccurred(cros::mojom::ObserverErrorType::READ_FAILED);
} else {
lux_value = it->second;
VLOG(1) << "Read lux " << lux_value.value() << " from channel "
<< iio_channel_ids_[illuminance_index_.value()];
if (++num_recovery_reads_ == kNumRecoveryReads) {
num_recovery_reads_ = 0;
if (num_failed_reads_ > 0)
--num_failed_reads_;
}
}
if (color_channels_enabled_)
color_temperature = GetColorTemperature(sample);
if (lux_value.has_value() || color_temperature.has_value())
set_lux_callback_.Run(lux_value, color_temperature);
}
void AmbientLightSensorDelegateMojo::OnErrorOccurred(
cros::mojom::ObserverErrorType type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
switch (type) {
case cros::mojom::ObserverErrorType::ALREADY_STARTED:
LOG(ERROR) << "Device " << iio_device_id_
<< ": Another observer has already started to read samples";
Reset();
break;
case cros::mojom::ObserverErrorType::FREQUENCY_INVALID:
LOG(ERROR) << "Device " << iio_device_id_
<< ": Observer started with an invalid frequency";
if (sensor_device_remote_.is_bound()) {
sensor_device_remote_->SetFrequency(
kFrequencyInHz,
base::BindOnce(
&AmbientLightSensorDelegateMojo::SetFrequencyCallback,
weak_factory_.GetWeakPtr()));
}
break;
case cros::mojom::ObserverErrorType::NO_ENABLED_CHANNELS:
LOG(ERROR) << "Device " << iio_device_id_
<< ": Observer started with no channels enabled";
if (sensor_device_remote_.is_bound()) {
sensor_device_remote_->SetChannelsEnabled(
channel_indices_, true,
base::BindOnce(
&AmbientLightSensorDelegateMojo::SetChannelsEnabledCallback,
weak_factory_.GetWeakPtr()));
}
break;
case cros::mojom::ObserverErrorType::SET_FREQUENCY_IO_FAILED:
LOG(ERROR) << "Device " << iio_device_id_
<< ": Failed to set frequency to the physical device";
break;
case cros::mojom::ObserverErrorType::GET_FD_FAILED:
LOG(ERROR) << "Device " << iio_device_id_
<< ": Failed to get the device's fd to poll on";
break;
case cros::mojom::ObserverErrorType::READ_FAILED:
LOG(ERROR) << "Device " << iio_device_id_ << ": Failed to read a sample";
ReadError();
break;
case cros::mojom::ObserverErrorType::READ_TIMEOUT:
LOG(ERROR) << "Device " << iio_device_id_ << ": A read timed out";
ReadError();
break;
default:
LOG(ERROR) << "Device " << iio_device_id_ << ": error "
<< static_cast<int>(type);
break;
}
}
AmbientLightSensorDelegateMojo::AmbientLightSensorDelegateMojo(
int iio_device_id,
mojo::Remote<cros::mojom::SensorDevice> remote,
bool enable_color_support,
base::OnceClosure init_closure)
: iio_device_id_(iio_device_id),
sensor_device_remote_(std::move(remote)),
enable_color_support_(enable_color_support),
init_closure_(std::move(init_closure)) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(sensor_device_remote_.is_bound());
if (enable_color_support_) {
for (const ColorChannelInfo& channel : kColorChannelConfig)
channel_ids_to_enable_.insert(
GetChannelIlluminanceColorId(channel.rgb_name));
}
// Add the id of the clear channel.
channel_ids_to_enable_.insert(cros::mojom::kLightChannel);
GetAllChannelIds();
}
void AmbientLightSensorDelegateMojo::Reset() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LOG(INFO) << "Resetting AmbientLightSensorDelegateMojo";
receiver_.reset();
sensor_device_remote_.reset();
iio_channel_ids_.clear();
channel_indices_.clear();
illuminance_index_.reset();
color_indices_.clear();
num_failed_reads_ = 0;
num_recovery_reads_ = 0;
FinishInitialization();
}
void AmbientLightSensorDelegateMojo::GetAllChannelIds() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(sensor_device_remote_.is_bound());
sensor_device_remote_->GetAllChannelIds(
base::BindOnce(&AmbientLightSensorDelegateMojo::GetAllChannelIdsCallback,
weak_factory_.GetWeakPtr()));
}
void AmbientLightSensorDelegateMojo::GetAllChannelIdsCallback(
const std::vector<std::string>& iio_channel_ids) {
iio_channel_ids_ = iio_channel_ids;
channel_indices_.clear();
illuminance_index_.reset();
color_indices_.clear();
for (const auto& channel_id : channel_ids_to_enable_) {
for (int32_t j = 0; j < iio_channel_ids_.size(); ++j) {
if (channel_id != iio_channel_ids_[j])
continue;
if (!enable_color_support_) {
illuminance_index_ = j;
break;
}
auto* color_channel_info = ChannelIdToColorChannelInfo(channel_id);
if (color_channel_info) {
color_indices_[color_channel_info->type] = j;
} else {
// Cannot find the color lux channel, use the clear channel instead.
illuminance_index_ = j;
}
break;
}
}
if (!illuminance_index_.has_value()) {
LOG(ERROR) << "Lux channel not found";
Reset();
return;
}
if (enable_color_support_ &&
color_indices_.size() == base::size(kColorChannelConfig)) {
for (const auto& color_index : color_indices_)
channel_indices_.push_back(color_index.second);
} else {
enable_color_support_ = false;
}
channel_indices_.push_back(illuminance_index_.value());
StartReading();
}
void AmbientLightSensorDelegateMojo::StartReading() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(sensor_device_remote_.is_bound());
sensor_device_remote_->SetTimeout(0);
sensor_device_remote_->SetFrequency(
kFrequencyInHz,
base::BindOnce(&AmbientLightSensorDelegateMojo::SetFrequencyCallback,
weak_factory_.GetWeakPtr()));
sensor_device_remote_->SetChannelsEnabled(
channel_indices_, true,
base::BindOnce(
&AmbientLightSensorDelegateMojo::SetChannelsEnabledCallback,
weak_factory_.GetWeakPtr()));
sensor_device_remote_->StartReadingSamples(GetRemote());
}
mojo::PendingRemote<cros::mojom::SensorDeviceSamplesObserver>
AmbientLightSensorDelegateMojo::GetRemote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto pending_remote = receiver_.BindNewPipeAndPassRemote();
receiver_.set_disconnect_handler(
base::BindOnce(&AmbientLightSensorDelegateMojo::OnObserverDisconnect,
weak_factory_.GetWeakPtr()));
return pending_remote;
}
base::Optional<int> AmbientLightSensorDelegateMojo::GetColorValue(
const base::flat_map<int32_t, int64_t>& sample, ChannelType type) {
auto it_color_index = color_indices_.find(type);
if (it_color_index == color_indices_.end())
return base::nullopt;
auto it = sample.find(it_color_index->second);
if (it == sample.end())
return base::nullopt;
return it->second;
}
base::Optional<int> AmbientLightSensorDelegateMojo::GetColorTemperature(
const base::flat_map<int32_t, int64_t>& sample) {
std::map<ChannelType, int> readings;
for (const ColorChannelInfo& channel : kColorChannelConfig) {
auto value_opt = GetColorValue(sample, channel.type);
if (!value_opt.has_value())
continue;
readings[channel.type] = value_opt.value();
}
return AmbientLightSensorDelegate::CalculateColorTemperature(readings);
}
void AmbientLightSensorDelegateMojo::OnObserverDisconnect() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LOG(ERROR) << "OnObserverDisconnect error, assuming IIO Service crashes and "
"waiting for it to relaunch";
// Don't reset |sensor_device_remote_| so that AmbientLightSensorManager can
// get the disconnection.
receiver_.reset();
}
void AmbientLightSensorDelegateMojo::SetFrequencyCallback(double result_freq) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (result_freq > 0.0)
return;
LOG(ERROR) << "Failed to set frequency";
Reset();
}
void AmbientLightSensorDelegateMojo::SetChannelsEnabledCallback(
const std::vector<int32_t>& failed_indices) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(illuminance_index_.has_value());
for (int32_t index : failed_indices) {
if (index == illuminance_index_.value()) {
LOG(ERROR) << "Failed to enable " << cros::mojom::kLightChannel
<< ". Giving up on reading samples";
Reset();
return;
}
LOG(ERROR) << "Failed to enable channel: " << iio_channel_ids_[index];
}
if (enable_color_support_ && failed_indices.empty())
color_channels_enabled_ = true;
FinishInitialization();
}
void AmbientLightSensorDelegateMojo::ReadError() {
if (++num_failed_reads_ < kNumFailedReadsBeforeGivingUp) {
LOG(ERROR) << "ReadSamples error #" << num_failed_reads_ << " occurred";
return;
}
// reset counts
num_failed_reads_ = num_recovery_reads_ = 0;
LOG(ERROR) << "Too many failed reads";
Reset();
}
void AmbientLightSensorDelegateMojo::FinishInitialization() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (init_closure_)
std::move(init_closure_).Run();
}
} // namespace system
} // namespace power_manager