Commit ab75f12d authored by Harvey Yang's avatar Harvey Yang Committed by Chromium LUCI CQ

auto_screen_brightness: Add LightProviderMojo and LightSamplesObserver

This commit adds and uses LightProviderMojo and LightSamplesObserver
when IIO Service is available.
LightProviderMojo will request information and the target light sensor's
samples from IIO Service instead of script: backlight_tool and
reading raw values directly from sysfs.

BUG=b:173485266, b:176871193
TEST=unit tests and run on nightfury(hatch)

Change-Id: I8b7e23677def14261296378a9d205f3d310b47d3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2568999Reviewed-by: default avatarThanh Nguyen <thanhdng@chromium.org>
Commit-Queue: Cheng-Hao Yang <chenghaoyang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845988}
parent dda70072
...@@ -7,6 +7,7 @@ import("//build/config/features.gni") ...@@ -7,6 +7,7 @@ import("//build/config/features.gni")
import("//build/config/python.gni") import("//build/config/python.gni")
import("//build/config/ui.gni") import("//build/config/ui.gni")
import("//chromeos/components/chromebox_for_meetings/buildflags/buildflags.gni") import("//chromeos/components/chromebox_for_meetings/buildflags/buildflags.gni")
import("//chromeos/components/sensors/buildflags.gni")
import("//extensions/buildflags/buildflags.gni") import("//extensions/buildflags/buildflags.gni")
import("//media/media_options.gni") import("//media/media_options.gni")
import("//printing/buildflags/buildflags.gni") import("//printing/buildflags/buildflags.gni")
...@@ -139,6 +140,7 @@ source_set("chromeos") { ...@@ -139,6 +140,7 @@ source_set("chromeos") {
"//chromeos/components/scanning", "//chromeos/components/scanning",
"//chromeos/components/scanning/mojom", "//chromeos/components/scanning/mojom",
"//chromeos/components/sensors", "//chromeos/components/sensors",
"//chromeos/components/sensors:buildflags",
"//chromeos/components/sensors/mojom", "//chromeos/components/sensors/mojom",
"//chromeos/components/smbfs", "//chromeos/components/smbfs",
"//chromeos/components/smbfs/mojom", "//chromeos/components/smbfs/mojom",
...@@ -3062,6 +3064,19 @@ source_set("chromeos") { ...@@ -3062,6 +3064,19 @@ source_set("chromeos") {
"extensions/wallpaper_private_api.h", "extensions/wallpaper_private_api.h",
] ]
if (use_iioservice) {
sources -= [
"power/auto_screen_brightness/als_file_reader.cc",
"power/auto_screen_brightness/als_file_reader.h",
]
sources += [
"power/auto_screen_brightness/light_provider_mojo.cc",
"power/auto_screen_brightness/light_provider_mojo.h",
"power/auto_screen_brightness/light_samples_observer.cc",
"power/auto_screen_brightness/light_samples_observer.h",
]
}
if (use_cups) { if (use_cups) {
configs += [ "//printing:cups" ] configs += [ "//printing:cups" ]
deps += [ deps += [
...@@ -4210,6 +4225,15 @@ source_set("unit_tests") { ...@@ -4210,6 +4225,15 @@ source_set("unit_tests") {
"//ui/events/devices:test_support", "//ui/events/devices:test_support",
"//ui/shell_dialogs", "//ui/shell_dialogs",
] ]
if (use_iioservice) {
sources -= [ "power/auto_screen_brightness/als_file_reader_unittest.cc" ]
sources += [
"power/auto_screen_brightness/light_provider_mojo_unittest.cc",
"power/auto_screen_brightness/light_samples_observer_unittest.cc",
]
deps += [ "//chromeos/components/sensors:test_support" ]
}
if (use_udev) { if (use_udev) {
deps += [ "//device/udev_linux:test_support" ] deps += [ "//device/udev_linux:test_support" ]
} }
......
...@@ -15,7 +15,12 @@ ...@@ -15,7 +15,12 @@
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/task/post_task.h" #include "base/task/post_task.h"
#include "base/task/thread_pool.h" #include "base/task/thread_pool.h"
#include "chromeos/components/sensors/buildflags.h"
#if BUILDFLAG(USE_IIOSERVICE)
#include "chrome/browser/chromeos/power/auto_screen_brightness/light_provider_mojo.h"
#else // !BUILDFLAG(USE_IIOSERVICE)
#include "chrome/browser/chromeos/power/auto_screen_brightness/als_file_reader.h" #include "chrome/browser/chromeos/power/auto_screen_brightness/als_file_reader.h"
#endif // BUILDFLAG(USE_IIOSERVICE)
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
namespace chromeos { namespace chromeos {
...@@ -90,9 +95,14 @@ void AlsReader::OnNumAlsRetrieved(int num_als) { ...@@ -90,9 +95,14 @@ void AlsReader::OnNumAlsRetrieved(int num_als) {
return; return;
} }
#if BUILDFLAG(USE_IIOSERVICE)
blocking_task_runner_.reset();
provider_ = std::make_unique<LightProviderMojo>(this, num_als > 1);
#else // !BUILDFLAG(USE_IIOSERVICE)
auto provider = std::make_unique<AlsFileReader>(this); auto provider = std::make_unique<AlsFileReader>(this);
provider->Init(std::move(blocking_task_runner_)); provider->Init(std::move(blocking_task_runner_));
provider_ = std::move(provider); provider_ = std::move(provider);
#endif // BUILDFLAG(USE_IIOSERVICE)
} }
void AlsReader::SetLux(int lux) { void AlsReader::SetLux(int lux) {
......
...@@ -20,7 +20,11 @@ class AlsFileReader; ...@@ -20,7 +20,11 @@ class AlsFileReader;
class AlsReaderTest; class AlsReaderTest;
class FakeLightProvider; class FakeLightProvider;
class LightProviderInterface; class LightProviderInterface;
class LightProviderMojo;
class LightSamplesObserver;
// If IIO Service is present, it uses LightProviderMojo as the implementation of
// LightProviderInterface; otherwise, it uses AlsFileReader.
class AlsReader { class AlsReader {
public: public:
// Status of AlsReader initialization. // Status of AlsReader initialization.
...@@ -63,6 +67,8 @@ class AlsReader { ...@@ -63,6 +67,8 @@ class AlsReader {
friend AlsFileReader; friend AlsFileReader;
friend AlsReaderTest; friend AlsReaderTest;
friend FakeLightProvider; friend FakeLightProvider;
friend LightProviderMojo;
friend LightSamplesObserver;
// Called when we've retrieved the number of ALS present. // Called when we've retrieved the number of ALS present.
void OnNumAlsRetrieved(int num_als); void OnNumAlsRetrieved(int num_als);
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_LIGHT_PROVIDER_MOJO_H_
#define CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_LIGHT_PROVIDER_MOJO_H_
#include <map>
#include <string>
#include <vector>
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "chrome/browser/chromeos/power/auto_screen_brightness/als_reader.h"
#include "chrome/browser/chromeos/power/auto_screen_brightness/light_samples_observer.h"
#include "chromeos/components/sensors/mojom/cros_sensor_service.mojom.h"
#include "chromeos/components/sensors/mojom/sensor.mojom.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
namespace chromeos {
namespace power {
namespace auto_screen_brightness {
// Used when IIO Service is present. It registers to Sensor Hal Dispatcher as a
// sensor client and waits for the connection to CrOS IIO Service. Once
// connected, it asks for samples of the light sensor (cros-ec-light or
// acpi-als).
class LightProviderMojo : public LightProviderInterface,
public chromeos::sensors::mojom::SensorHalClient {
public:
LightProviderMojo(AlsReader* als_reader, bool has_several_light_sensors);
LightProviderMojo(const LightProviderMojo&) = delete;
LightProviderMojo& operator=(const LightProviderMojo&) = delete;
~LightProviderMojo() override;
// chromeos::sensors::mojom::SensorHalClient:
void SetUpChannel(mojo::PendingRemote<chromeos::sensors::mojom::SensorService>
pending_remote) override;
private:
struct LightData {
LightData();
~LightData();
// Something wrong with attributes of this light sensor or simply not needed
// if true.
bool ignored = false;
base::Optional<std::string> name;
base::Optional<bool> on_lid;
// Temporarily stores the remote, waiting for its attributes information.
// It'll be passed to LightProviderMojo' constructor as an argument after
// all information is collected, if this sensor is needed.
mojo::Remote<chromeos::sensors::mojom::SensorDevice> remote;
};
// Fails the initialization due to an error and notify the observers.
void FailedToInitialize();
// Registers chromeos::sensors::mojom::SensorHalClient to Sensor Hal
// Dispatcher, waiting for the Mojo connection to IIO Service.
void RegisterSensorClient();
void OnSensorHalClientFailure();
void OnSensorServiceDisconnect();
void ResetSensorService();
// Callback of GetDeviceIds(LIGHT), containing all iio_device_ids of light
// sensors.
void GetLightIdsCallback(const std::vector<int32_t>& light_ids);
// Creates the Mojo channel for the light sensor, and requests the light
// sensor's required attributes before determining which one to be used.
void RegisterLightWithId(int32_t id);
void GetNameLocationCallback(
int32_t id,
const std::vector<base::Optional<std::string>>& values);
void GetNameCallback(int32_t id,
const std::vector<base::Optional<std::string>>& values);
// Ignores the light with |id| due to some errors of it's attributes.
void IgnoreLight(int32_t id);
mojo::Remote<chromeos::sensors::mojom::SensorDevice> GetSensorDeviceRemote(
int32_t id);
void OnLightRemoteDisconnect(int32_t id);
void DetermineLightSensor(int32_t id);
void SetupLightSamplesObserver();
// Needs cros-ec-light on the lid if true; prefer cros-ec-light than acpi-als
// if false.
bool has_several_light_sensors_;
// The Mojo channel connecting to Sensor Hal Dispatcher.
mojo::Receiver<chromeos::sensors::mojom::SensorHalClient> sensor_hal_client_{
this};
// The Mojo channel to query and request for devices.
mojo::Remote<chromeos::sensors::mojom::SensorService> sensor_service_remote_;
// First is the light sensor's iio device id, second is it's data and mojo
// remote.
std::map<int32_t, LightData> lights_;
// The device id of light to be used.
base::Optional<int32_t> light_device_id_;
// The observer that waits for the wanted light sensor's samples and sends
// them to |als_reader_|.
std::unique_ptr<LightSamplesObserver> observer_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<LightProviderMojo> weak_ptr_factory_{this};
};
} // namespace auto_screen_brightness
} // namespace power
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_LIGHT_PROVIDER_MOJO_H_
// Copyright 2020 The Chromium 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 "chrome/browser/chromeos/power/auto_screen_brightness/light_provider_mojo.h"
#include <map>
#include <memory>
#include <utility>
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "chrome/browser/chromeos/power/auto_screen_brightness/fake_observer.h"
#include "chromeos/components/sensors/fake_sensor_device.h"
#include "chromeos/components/sensors/fake_sensor_hal_server.h"
#include "chromeos/components/sensors/sensor_hal_dispatcher.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace power {
namespace auto_screen_brightness {
namespace {
constexpr int32_t kFakeAcpiAlsId = 1;
constexpr int32_t kFakeBaseLightId = 2;
constexpr int32_t kFakeLidLightId = 3;
constexpr int64_t kFakeSampleData = 50;
const char kWrongLocation[5] = "lidd";
constexpr char kCrosECLightName[] = "cros-ec-light";
constexpr char kAcpiAlsName[] = "acpi-als";
} // namespace
class LightProviderMojoTest : public testing::Test {
protected:
void SetUp() override {
chromeos::sensors::SensorHalDispatcher::Initialize();
sensor_hal_server_ =
std::make_unique<chromeos::sensors::FakeSensorHalServer>();
als_reader_ = std::make_unique<AlsReader>();
als_reader_->AddObserver(&fake_observer_);
}
void TearDown() override {
chromeos::sensors::SensorHalDispatcher::Shutdown();
}
void SetProvider(bool has_several_light_sensors) {
provider_ = std::make_unique<LightProviderMojo>(als_reader_.get(),
has_several_light_sensors);
}
void AddDevice(int32_t iio_device_id,
const base::Optional<std::string> name,
const base::Optional<std::string> location) {
std::vector<chromeos::sensors::FakeSensorDevice::ChannelData> channels_data(
1);
channels_data[0].id = chromeos::sensors::mojom::kLightChannel;
channels_data[0].sample_data = kFakeSampleData;
std::unique_ptr<chromeos::sensors::FakeSensorDevice> sensor_device(
new chromeos::sensors::FakeSensorDevice(std::move(channels_data)));
sensor_devices_[iio_device_id] = sensor_device.get();
if (name.has_value()) {
sensor_device->SetAttribute(chromeos::sensors::mojom::kDeviceName,
name.value());
}
if (location.has_value()) {
sensor_device->SetAttribute(chromeos::sensors::mojom::kLocation,
location.value());
}
sensor_hal_server_->GetSensorService()->SetDevice(
iio_device_id,
std::set<chromeos::sensors::mojom::DeviceType>{
chromeos::sensors::mojom::DeviceType::LIGHT},
std::move(sensor_device));
}
void CheckValues(int32_t iio_device_id) {
EXPECT_TRUE(sensor_hal_server_->GetSensorService()->is_bound());
EXPECT_TRUE(sensor_devices_.find(iio_device_id) != sensor_devices_.end());
EXPECT_TRUE(sensor_devices_[iio_device_id]->HasReceivers());
EXPECT_EQ(fake_observer_.status(), AlsReader::AlsInitStatus::kSuccess);
EXPECT_EQ(fake_observer_.num_received_ambient_lights(), ++num_samples_);
EXPECT_EQ(fake_observer_.ambient_light(), kFakeSampleData);
}
FakeObserver fake_observer_;
std::unique_ptr<chromeos::sensors::FakeSensorHalServer> sensor_hal_server_;
std::unique_ptr<AlsReader> als_reader_;
std::unique_ptr<LightProviderMojo> provider_;
std::map<int32_t, chromeos::sensors::FakeSensorDevice*> sensor_devices_;
int num_samples_ = 0;
base::test::SingleThreadTaskEnvironment task_environment;
};
TEST_F(LightProviderMojoTest, GetSamplesWithOneSensor) {
SetProvider(/*has_several_light_sensors=*/false);
AddDevice(kFakeAcpiAlsId, kAcpiAlsName, base::nullopt);
chromeos::sensors::SensorHalDispatcher::GetInstance()->RegisterServer(
sensor_hal_server_->PassRemote());
// Wait until a sample is received.
base::RunLoop().RunUntilIdle();
CheckValues(kFakeAcpiAlsId);
}
TEST_F(LightProviderMojoTest, AssumingAcpiAlsWithoutDeviceNameWithOneSensor) {
SetProvider(/*has_several_light_sensors=*/false);
AddDevice(kFakeAcpiAlsId, base::nullopt, base::nullopt);
chromeos::sensors::SensorHalDispatcher::GetInstance()->RegisterServer(
sensor_hal_server_->PassRemote());
// Wait until a sample is received.
base::RunLoop().RunUntilIdle();
CheckValues(kFakeAcpiAlsId);
}
TEST_F(LightProviderMojoTest, PreferCrosECLightWithOneSensor) {
SetProvider(/*has_several_light_sensors=*/false);
AddDevice(kFakeAcpiAlsId, kAcpiAlsName, base::nullopt);
AddDevice(kFakeLidLightId, kCrosECLightName, base::nullopt);
chromeos::sensors::SensorHalDispatcher::GetInstance()->RegisterServer(
sensor_hal_server_->PassRemote());
// Wait until a sample is received.
base::RunLoop().RunUntilIdle();
CheckValues(kFakeLidLightId);
}
TEST_F(LightProviderMojoTest, InvalidLocationWithSeveralLightSensors) {
SetProvider(/*has_several_light_sensors=*/true);
AddDevice(kFakeLidLightId, kCrosECLightName, kWrongLocation);
chromeos::sensors::SensorHalDispatcher::GetInstance()->RegisterServer(
sensor_hal_server_->PassRemote());
// Wait until the mojo connection is reset.
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(sensor_hal_server_->GetSensorService()->is_bound());
EXPECT_EQ(fake_observer_.status(),
AlsReader::AlsInitStatus::kIncorrectConfig);
}
TEST_F(LightProviderMojoTest, GetSamplesFromLidLightsSeveralLightSensors) {
SetProvider(/*has_several_light_sensors=*/true);
AddDevice(kFakeAcpiAlsId, kAcpiAlsName, base::nullopt);
AddDevice(kFakeBaseLightId, kCrosECLightName,
chromeos::sensors::mojom::kLocationBase);
AddDevice(kFakeLidLightId, kCrosECLightName,
chromeos::sensors::mojom::kLocationLid);
chromeos::sensors::SensorHalDispatcher::GetInstance()->RegisterServer(
sensor_hal_server_->PassRemote());
// Wait until a sample is received.
base::RunLoop().RunUntilIdle();
CheckValues(kFakeLidLightId);
// Simulate a disconnection of the accelerometer's mojo channel in IIO
// Service.
AddDevice(kFakeLidLightId, kCrosECLightName,
chromeos::sensors::mojom::kLocationLid);
// Wait until the disconnection is done.
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(sensor_hal_server_->GetSensorService()->is_bound());
// Simulate a disconnection of IIO Service.
sensor_hal_server_->GetSensorService()->OnServiceDisconnect();
sensor_hal_server_->OnServerDisconnect();
// Wait until the disconnect arrives at the dispatcher.
base::RunLoop().RunUntilIdle();
chromeos::sensors::SensorHalDispatcher::GetInstance()->RegisterServer(
sensor_hal_server_->PassRemote());
// Wait until samples are received.
base::RunLoop().RunUntilIdle();
CheckValues(kFakeLidLightId);
}
} // namespace auto_screen_brightness
} // namespace power
} // namespace chromeos
// Copyright 2020 The Chromium 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 "chrome/browser/chromeos/power/auto_screen_brightness/light_samples_observer.h"
#include <utility>
#include "base/bind.h"
#include "chrome/browser/chromeos/power/auto_screen_brightness/utils.h"
namespace chromeos {
namespace power {
namespace auto_screen_brightness {
LightSamplesObserver::LightSamplesObserver(
AlsReader* als_reader,
mojo::Remote<chromeos::sensors::mojom::SensorDevice> sensor_device_remote)
: als_reader_(als_reader),
sensor_device_remote_(std::move(sensor_device_remote)) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(sensor_device_remote_.is_bound());
sensor_device_remote_->GetAllChannelIds(
base::BindOnce(&LightSamplesObserver::GetAllChannelIdsCallback,
weak_ptr_factory_.GetWeakPtr()));
}
LightSamplesObserver::~LightSamplesObserver() = default;
void LightSamplesObserver::OnSampleUpdated(
const base::flat_map<int32_t, int64_t>& sample) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(channel_index_.has_value());
DCHECK(als_reader_);
const auto it = sample.find(channel_index_.value());
if (it == sample.end()) {
LogDataError(DataError::kAlsValue);
return;
}
als_reader_->SetLux(it->second);
}
void LightSamplesObserver::OnErrorOccurred(
chromeos::sensors::mojom::ObserverErrorType type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
switch (type) {
case chromeos::sensors::mojom::ObserverErrorType::ALREADY_STARTED:
LOG(ERROR) << "Another observer has already started to read samples";
Reset();
break;
case chromeos::sensors::mojom::ObserverErrorType::FREQUENCY_INVALID:
LOG(ERROR) << "Observer started with an invalid frequency";
SetFrequency();
break;
case chromeos::sensors::mojom::ObserverErrorType::NO_ENABLED_CHANNELS:
LOG(ERROR) << "Observer started with no channels enabled";
SetChannelsEnabled();
break;
case chromeos::sensors::mojom::ObserverErrorType::SET_FREQUENCY_IO_FAILED:
LOG(ERROR) << "Failed to set frequency to the physical device";
break;
case chromeos::sensors::mojom::ObserverErrorType::GET_FD_FAILED:
LOG(ERROR) << "Failed to get the device's fd to poll on";
break;
case chromeos::sensors::mojom::ObserverErrorType::READ_FAILED:
LOG(ERROR) << "Failed to read a sample";
break;
case chromeos::sensors::mojom::ObserverErrorType::READ_TIMEOUT:
LOG(ERROR) << "A read timed out";
break;
default:
LOG(ERROR) << "Error: " << static_cast<int>(type);
break;
}
}
void LightSamplesObserver::Reset() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(als_reader_);
LOG(ERROR) << "Resetting LightSamplesObserver";
receiver_.reset();
sensor_device_remote_.reset();
LogDataError(DataError::kMojoSamplesObserver);
}
void LightSamplesObserver::GetAllChannelIdsCallback(
const std::vector<std::string>& iio_channel_ids) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(sensor_device_remote_.is_bound());
for (int32_t i = 0; i < iio_channel_ids.size(); ++i) {
if (iio_channel_ids[i].compare(chromeos::sensors::mojom::kLightChannel) ==
0) {
channel_index_ = i;
break;
}
}
if (!channel_index_.has_value()) {
LOG(ERROR) << "Missing the available lux channel";
Reset();
return;
}
StartReading();
}
void LightSamplesObserver::StartReading() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(sensor_device_remote_.is_bound());
sensor_device_remote_->SetTimeout(0);
SetFrequency();
SetChannelsEnabled();
sensor_device_remote_->StartReadingSamples(GetPendingRemote());
}
mojo::PendingRemote<chromeos::sensors::mojom::SensorDeviceSamplesObserver>
LightSamplesObserver::GetPendingRemote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto pending_remote = receiver_.BindNewPipeAndPassRemote();
receiver_.set_disconnect_handler(
base::BindOnce(&LightSamplesObserver::OnObserverDisconnect,
weak_ptr_factory_.GetWeakPtr()));
return pending_remote;
}
void LightSamplesObserver::OnObserverDisconnect() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LOG(ERROR) << "OnObserverDisconnect";
// Don't reset |sensor_device_remote_| so that LightProviderMojo can get the
// disconnection.
receiver_.reset();
}
void LightSamplesObserver::SetFrequency() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sensor_device_remote_->SetFrequency(
AlsReader::kAlsPollFrequency,
base::BindOnce(&LightSamplesObserver::SetFrequencyCallback,
weak_ptr_factory_.GetWeakPtr()));
}
void LightSamplesObserver::SetFrequencyCallback(double result_frequency) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (result_frequency > 0.0)
return;
LOG(ERROR) << "Failed to set frequency: " << AlsReader::kAlsPollFrequency;
Reset();
}
void LightSamplesObserver::SetChannelsEnabled() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(channel_index_.has_value());
sensor_device_remote_->SetChannelsEnabled(
std::vector<int32_t>{channel_index_.value()},
/*enable=*/true,
base::BindOnce(&LightSamplesObserver::SetChannelsEnabledCallback,
weak_ptr_factory_.GetWeakPtr()));
}
void LightSamplesObserver::SetChannelsEnabledCallback(
const std::vector<int32_t>& failed_indices) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (const int32_t index : failed_indices) {
if (index == channel_index_) {
LOG(ERROR) << "Failed to enable the lux channel: "
<< channel_index_.value();
Reset();
return;
}
LOG(WARNING) << "Failed to enable an unnecessary channel with index: "
<< index;
}
}
} // namespace auto_screen_brightness
} // namespace power
} // namespace chromeos
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_LIGHT_SAMPLES_OBSERVER_H_
#define CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_LIGHT_SAMPLES_OBSERVER_H_
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "chrome/browser/chromeos/power/auto_screen_brightness/als_reader.h"
#include "chromeos/components/sensors/mojom/sensor.mojom.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
namespace chromeos {
namespace power {
namespace auto_screen_brightness {
// LightSamplesObserver waits for a light sensor's samples from IIO Service.
// When a sample is updated from IIO Service, it's sent to the AlsReader and
// further notifies observers.
class LightSamplesObserver
: public chromeos::sensors::mojom::SensorDeviceSamplesObserver {
public:
LightSamplesObserver(AlsReader* als_reader,
mojo::Remote<chromeos::sensors::mojom::SensorDevice>
sensor_device_remote);
LightSamplesObserver(const LightSamplesObserver&) = delete;
LightSamplesObserver& operator=(const LightSamplesObserver&) = delete;
~LightSamplesObserver() override;
// chromeos::sensors::mojom::SensorDeviceSamplesObserver overrides:
void OnSampleUpdated(const base::flat_map<int32_t, int64_t>& sample) override;
void OnErrorOccurred(
chromeos::sensors::mojom::ObserverErrorType type) override;
private:
void Reset();
void GetAllChannelIdsCallback(
const std::vector<std::string>& iio_channel_ids);
void StartReading();
mojo::PendingRemote<chromeos::sensors::mojom::SensorDeviceSamplesObserver>
GetPendingRemote();
void OnObserverDisconnect();
void SetFrequency();
void SetFrequencyCallback(double result_frequency);
void SetChannelsEnabled();
void SetChannelsEnabledCallback(const std::vector<int32_t>& failed_indices);
AlsReader* als_reader_;
mojo::Remote<chromeos::sensors::mojom::SensorDevice> sensor_device_remote_;
mojo::Receiver<chromeos::sensors::mojom::SensorDeviceSamplesObserver>
receiver_{this};
// Channel index of the target channel: "illuminance".
base::Optional<int32_t> channel_index_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<LightSamplesObserver> weak_ptr_factory_{this};
};
} // namespace auto_screen_brightness
} // namespace power
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_LIGHT_SAMPLES_OBSERVER_H_
// Copyright 2020 The Chromium 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 "chrome/browser/chromeos/power/auto_screen_brightness/light_samples_observer.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "chrome/browser/chromeos/power/auto_screen_brightness/fake_observer.h"
#include "chrome/browser/chromeos/power/auto_screen_brightness/utils.h"
#include "chromeos/components/sensors/fake_sensor_device.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace power {
namespace auto_screen_brightness {
namespace {
constexpr int64_t kFakeSampleData = 50;
constexpr int64_t kFakeColorSampleData = 60;
constexpr char kIlluminanceColorChannels[][18] = {
"illuminance_red", "illuminance_green", "illuminance_blue"};
} // namespace
class LightSamplesObserverTest : public testing::Test {
protected:
void SetChannels(bool is_color_sensor) {
std::vector<chromeos::sensors::FakeSensorDevice::ChannelData> channels_data;
chromeos::sensors::FakeSensorDevice::ChannelData illuminance_data;
illuminance_data.id = chromeos::sensors::mojom::kLightChannel;
illuminance_data.sample_data = kFakeSampleData;
channels_data.push_back(std::move(illuminance_data));
if (is_color_sensor) {
for (size_t i = 0; i < base::size(kIlluminanceColorChannels); ++i) {
illuminance_data.id = kIlluminanceColorChannels[i];
illuminance_data.sample_data = kFakeColorSampleData;
channels_data.push_back(std::move(illuminance_data));
}
}
sensor_device_ = std::make_unique<chromeos::sensors::FakeSensorDevice>(
std::move(channels_data));
}
void SetProvider(mojo::Remote<chromeos::sensors::mojom::SensorDevice> light) {
als_reader_ = std::make_unique<AlsReader>();
observer_ = std::make_unique<LightSamplesObserver>(als_reader_.get(),
std::move(light));
als_reader_->AddObserver(&fake_observer_);
}
void DisableIlluminanceChannel() {
sensor_device_->SetChannelsEnabled(
{0}, false,
base::BindOnce(&LightSamplesObserverTest::SetChannelsEnabledCallback,
base::Unretained(this)));
}
void SetChannelsEnabledCallback(const std::vector<int32_t>& failed_indices) {
EXPECT_EQ(failed_indices.size(), 0u);
}
std::unique_ptr<chromeos::sensors::FakeSensorDevice> sensor_device_;
FakeObserver fake_observer_;
std::unique_ptr<LightSamplesObserver> observer_;
std::unique_ptr<AlsReader> als_reader_;
base::HistogramTester histogram_tester_;
base::test::SingleThreadTaskEnvironment task_environment_;
};
TEST_F(LightSamplesObserverTest, MissingChannels) {
sensor_device_ = std::make_unique<chromeos::sensors::FakeSensorDevice>(
std::vector<chromeos::sensors::FakeSensorDevice::ChannelData>{});
mojo::Remote<chromeos::sensors::mojom::SensorDevice> light;
sensor_device_->AddReceiver(light.BindNewPipeAndPassReceiver());
SetProvider(std::move(light));
// Wait until the mojo connection is reset.
base::RunLoop().RunUntilIdle();
histogram_tester_.ExpectUniqueSample(
"AutoScreenBrightness.DataError",
static_cast<int>(DataError::kMojoSamplesObserver), 1);
EXPECT_FALSE(sensor_device_->HasReceivers());
EXPECT_EQ(fake_observer_.num_received_ambient_lights(), 0);
}
TEST_F(LightSamplesObserverTest, StartReadingTwiceError) {
SetChannels(false);
mojo::Remote<chromeos::sensors::mojom::SensorDevice> light;
sensor_device_->AddReceiver(light.BindNewPipeAndPassReceiver());
mojo::PendingRemote<chromeos::sensors::mojom::SensorDeviceSamplesObserver>
pending_remote;
auto null_receiver = pending_remote.InitWithNewPipeAndPassReceiver();
light->StartReadingSamples(std::move(pending_remote));
SetProvider(std::move(light));
// Wait until the mojo connection is reset.
base::RunLoop().RunUntilIdle();
histogram_tester_.ExpectUniqueSample(
"AutoScreenBrightness.DataError",
static_cast<int>(DataError::kMojoSamplesObserver), 1);
EXPECT_FALSE(sensor_device_->HasReceivers());
EXPECT_EQ(fake_observer_.num_received_ambient_lights(), 0);
}
TEST_F(LightSamplesObserverTest, GetSamplesWithoutColorChannels) {
SetChannels(false);
mojo::Remote<chromeos::sensors::mojom::SensorDevice> light;
sensor_device_->AddReceiver(light.BindNewPipeAndPassReceiver());
SetProvider(std::move(light));
// Wait until a sample is received.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(sensor_device_->HasReceivers());
EXPECT_EQ(fake_observer_.num_received_ambient_lights(), 1);
EXPECT_EQ(fake_observer_.ambient_light(), kFakeSampleData);
DisableIlluminanceChannel();
// Wait until a sample is received.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(sensor_device_->HasReceivers());
// The updated sample is not sent to |OnAmbientLightUpdated(lux)|.
EXPECT_EQ(fake_observer_.num_received_ambient_lights(), 1);
// Simulate a disconnection of the observer's mojo channel in IIO Service.
sensor_device_->StopReadingSamples();
// Wait until the disconnection is done.
base::RunLoop().RunUntilIdle();
// OnObserverDisconnect shouldn't reset SensorDevice's mojo endpoint so that
// LightProviderMojo can get the disconnection.
EXPECT_TRUE(sensor_device_->HasReceivers());
}
TEST_F(LightSamplesObserverTest, GetSamplesWithColorChannels) {
SetChannels(true);
mojo::Remote<chromeos::sensors::mojom::SensorDevice> light;
sensor_device_->AddReceiver(light.BindNewPipeAndPassReceiver());
SetProvider(std::move(light));
// Wait until a sample is received.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(sensor_device_->HasReceivers());
EXPECT_EQ(fake_observer_.num_received_ambient_lights(), 1);
EXPECT_EQ(fake_observer_.ambient_light(), kFakeSampleData);
base::flat_map<int32_t, int64_t> sample;
sample[2] = kFakeColorSampleData;
// Should ignore a sample without the target channel: illuminance.
observer_->OnSampleUpdated(sample);
EXPECT_EQ(fake_observer_.num_received_ambient_lights(), 1);
}
} // namespace auto_screen_brightness
} // namespace power
} // namespace chromeos
...@@ -20,7 +20,8 @@ namespace auto_screen_brightness { ...@@ -20,7 +20,8 @@ namespace auto_screen_brightness {
enum class DataError { enum class DataError {
kAlsValue = 0, kAlsValue = 0,
kBrightnessPercent = 1, kBrightnessPercent = 1,
kMaxValue = kBrightnessPercent kMojoSamplesObserver = 2,
kMaxValue = kMojoSamplesObserver
}; };
// Logs data errors to UMA. // Logs data errors to UMA.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment