Commit a609f4ac authored by Jia Meng's avatar Jia Meng Committed by Commit Bot

Reland "[On-device adaptive brightness] Implement an AlsReader to ambient light."


Original change's description:
> Revert "[On-device adaptive brightness] Implement an AlsReader to ambient light."
> 
> This reverts commit ea692f36.
> 
> Reason for revert: broke the linux-chromeos-dbg (https://ci.chromium.org/p/chromium/builders/luci.chromium.ci/linux-chromeos-dbg/7744)
> 
> Original change's description:
> > [On-device adaptive brightness] Implement an AlsReader to ambient light.
> > 
> > 
> > Bug: 881215
> > Change-Id: I1cd5bf7ea517e426b54df1f4ebbb5a843b991dbe
> > Reviewed-on: https://chromium-review.googlesource.com/1209000
> > Reviewed-by: Michael Martis <martis@chromium.org>
> > Reviewed-by: Dan Erat <derat@chromium.org>
> > Commit-Queue: Jia Meng <jiameng@chromium.org>
> > Cr-Commit-Position: refs/heads/master@{#589843}
> 
> TBR=derat@chromium.org,jiameng@chromium.org,martis@chromium.org
> 
> Change-Id: Id12029b7d3b46eaed672525fe0bcf96960ea143c
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Bug: 881215
> Reviewed-on: https://chromium-review.googlesource.com/1215603
> Reviewed-by: Jia Meng <jiameng@chromium.org>
> Commit-Queue: Jia Meng <jiameng@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#589850}

TBR=derat@chromium.org,jiameng@chromium.org,martis@chromium.org

Change-Id: I8f170731509b2b23d469519fd45d5876175111d5
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 881215
Reviewed-on: https://chromium-review.googlesource.com/1217962Reviewed-by: default avatarJia Meng <jiameng@chromium.org>
Commit-Queue: Jia Meng <jiameng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#590115}
parent 3197afbf
......@@ -1580,6 +1580,8 @@ source_set("chromeos") {
"policy/weekly_time/weekly_time_interval.h",
"policy/wildcard_login_checker.cc",
"policy/wildcard_login_checker.h",
"power/auto_screen_brightness/als_reader.cc",
"power/auto_screen_brightness/als_reader.h",
"power/cpu_data_collector.cc",
"power/cpu_data_collector.h",
"power/extension_event_observer.cc",
......@@ -2253,6 +2255,7 @@ source_set("unit_tests") {
"policy/weekly_time/time_utils_unittest.cc",
"policy/weekly_time/weekly_time_interval_unittest.cc",
"policy/weekly_time/weekly_time_unittest.cc",
"power/auto_screen_brightness/als_reader_unittest.cc",
"power/cpu_data_collector_unittest.cc",
"power/extension_event_observer_unittest.cc",
"power/ml/adaptive_screen_brightness_manager_unittest.cc",
......
// Copyright 2018 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/als_reader.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/macros.h"
#include "base/process/launch.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/task/post_task.h"
#include "content/public/browser/browser_thread.h"
namespace chromeos {
namespace power {
namespace auto_screen_brightness {
namespace {
// Returns whether the device has an ALS that we can use. This should run in
// another thread to be non-blocking to the main thread.
bool IsAlsEnabled() {
DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
base::CommandLine command_line{
base::FilePath(FILE_PATH_LITERAL("check_powerd_config"))};
command_line.AppendArg("--ambient_light_sensor");
int exit_code = 0;
std::string output; // Not used.
const bool result =
base::GetAppOutputWithExitCode(command_line, &output, &exit_code);
if (!result) {
LOG(ERROR) << "Cannot run check_powerd_config --ambient_light_sensor";
return false;
}
return exit_code == 0;
}
// Returns whether the ALS step config is what we need. This function is only
// called if an ALS is enabled. This should run in another thread to be
// non-blocking to the main thread.
// TODO(jiameng): we assume one specific device now, and only check if the
// number of steps is 7.
bool VerifyAlsConfig() {
DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
base::CommandLine command_line{
base::FilePath(FILE_PATH_LITERAL("check_powerd_config"))};
command_line.AppendArg("--internal_backlight_ambient_light_steps");
int exit_code = 0;
std::string output;
const bool result =
base::GetAppOutputWithExitCode(command_line, &output, &exit_code);
if (!result || exit_code != 0) {
LOG(ERROR) << "Cannot run check_powerd_config "
"--internal_backlight_ambient_light_steps";
return false;
}
const std::vector<base::StringPiece> num_steps = base::SplitStringPiece(
output, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
return num_steps.size() == 7;
}
// Returns ALS path. This should run in another thread to be non-blocking to the
// main thread.
std::string GetAlsPath() {
DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
base::CommandLine command_line{
base::FilePath(FILE_PATH_LITERAL("backlight_tool"))};
command_line.AppendArg("--get_ambient_light_path");
int exit_code = 0;
std::string output;
const bool result =
base::GetAppOutputWithExitCode(command_line, &output, &exit_code);
if (!result) {
LOG(ERROR) << "Cannot run backlight_tool --get_ambient_light_path";
return "";
}
if (exit_code != 0 || output.empty()) {
LOG(ERROR) << "Missing ambient light path";
return "";
}
return output;
}
// Reads ALS value from |ambient_light_path|. This should run in another thread
// to be non-blocking to the main thread.
std::string ReadAlsFromFile(const base::FilePath& ambient_light_path) {
DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
std::string data;
if (!base::ReadFileToString(ambient_light_path, &data)) {
LOG(ERROR) << "Cannot read ALS value";
return "";
}
return data;
}
} // namespace
constexpr int AlsReader::kMaxInitialAttempts;
AlsReader::AlsReader()
: als_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
{base::TaskPriority::BEST_EFFORT, base::MayBlock(),
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
weak_ptr_factory_(this) {}
AlsReader::~AlsReader() = default;
void AlsReader::AddObserver(Observer* const observer) {
DCHECK(observer);
observers_.AddObserver(observer);
}
void AlsReader::RemoveObserver(Observer* const observer) {
DCHECK(observer);
observers_.RemoveObserver(observer);
}
AlsReader::AlsInitStatus AlsReader::GetInitStatus() const {
return status_;
}
void AlsReader::Init() {
base::PostTaskAndReplyWithResult(
als_task_runner_.get(), FROM_HERE, base::BindOnce(&IsAlsEnabled),
base::BindOnce(&AlsReader::OnAlsEnableCheckDone, AsWeakPtr()));
}
void AlsReader::SetTaskRunnerForTesting(
const scoped_refptr<base::SequencedTaskRunner> task_runner) {
als_task_runner_ = task_runner;
als_timer_.SetTaskRunner(task_runner);
}
void AlsReader::InitForTesting(const base::FilePath& ambient_light_path) {
DCHECK(!ambient_light_path.empty());
ambient_light_path_ = ambient_light_path;
status_ = AlsInitStatus::kSuccess;
OnInitializationComplete();
ReadAlsPeriodically();
}
base::WeakPtr<AlsReader> AlsReader::AsWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
void AlsReader::OnAlsEnableCheckDone(const bool is_enabled) {
if (!is_enabled) {
status_ = AlsInitStatus::kDisabled;
OnInitializationComplete();
return;
}
base::PostTaskAndReplyWithResult(
als_task_runner_.get(), FROM_HERE, base::BindOnce(&VerifyAlsConfig),
base::BindOnce(&AlsReader::OnAlsConfigCheckDone, AsWeakPtr()));
}
void AlsReader::OnAlsConfigCheckDone(const bool is_config_valid) {
if (!is_config_valid) {
status_ = AlsInitStatus::kIncorrectConfig;
OnInitializationComplete();
return;
}
RetryAlsPath();
}
void AlsReader::OnAlsPathReadAttempted(const std::string& path) {
if (!path.empty()) {
ambient_light_path_ = base::FilePath(path);
status_ = AlsInitStatus::kSuccess;
OnInitializationComplete();
ReadAlsPeriodically();
return;
}
++num_failed_initialization_;
if (num_failed_initialization_ == kMaxInitialAttempts) {
status_ = AlsInitStatus::kMissingPath;
OnInitializationComplete();
return;
}
als_timer_.Start(FROM_HERE, kAlsFileCheckingInterval, this,
&AlsReader::RetryAlsPath);
}
void AlsReader::RetryAlsPath() {
base::PostTaskAndReplyWithResult(
als_task_runner_.get(), FROM_HERE, base::BindOnce(&GetAlsPath),
base::BindOnce(&AlsReader::OnAlsPathReadAttempted, AsWeakPtr()));
}
void AlsReader::OnInitializationComplete() {
DCHECK_NE(status_, AlsInitStatus::kInProgress);
for (auto& observer : observers_)
observer.OnAlsReaderInitialized(status_);
}
void AlsReader::ReadAlsPeriodically() {
base::PostTaskAndReplyWithResult(
als_task_runner_.get(), FROM_HERE,
base::BindOnce(&ReadAlsFromFile, ambient_light_path_),
base::BindOnce(&AlsReader::OnAlsRead, AsWeakPtr()));
}
void AlsReader::OnAlsRead(const std::string& data) {
std::string trimmed_data;
base::TrimWhitespaceASCII(data, base::TRIM_ALL, &trimmed_data);
int value = 0;
if (base::StringToInt(trimmed_data, &value)) {
for (auto& observer : observers_)
observer.OnAmbientLightUpdated(value);
}
als_timer_.Start(FROM_HERE, kAlsPollInterval, this,
&AlsReader::ReadAlsPeriodically);
}
} // namespace auto_screen_brightness
} // namespace power
} // namespace chromeos
// Copyright 2018 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_ALS_READER_H_
#define CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_ALS_READER_H_
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/sequenced_task_runner.h"
#include "base/task_runner_util.h"
#include "base/timer/timer.h"
namespace chromeos {
namespace power {
namespace auto_screen_brightness {
// AlsReader periodically reads lux values from the ambient light sensor (ALS)
// if powerd has been configured to use it.
class AlsReader {
public:
// ALS file location may not be ready immediately, so we retry every
// |kAlsFileCheckingInterval| until |kMaxInitialAttempts| is reached, then
// we give up.
static constexpr base::TimeDelta kAlsFileCheckingInterval =
base::TimeDelta::FromSeconds(1);
static constexpr int kMaxInitialAttempts = 20;
// Interval for polling ambient light values.
static constexpr base::TimeDelta kAlsPollInterval =
base::TimeDelta::FromSeconds(1);
// Status of AlsReader initialization.
enum class AlsInitStatus {
kSuccess = 0,
kInProgress = 1,
kDisabled = 2,
kIncorrectConfig = 3,
kMissingPath = 4,
kMaxValue = kMissingPath
};
// Observers should take WeakPtr of AlsReader and remove themselves in
// observers' destructors if AlsReader hasn't be destructed.
class Observer : public base::CheckedObserver {
public:
Observer() = default;
~Observer() override = default;
virtual void OnAmbientLightUpdated(int lux) = 0;
virtual void OnAlsReaderInitialized(AlsInitStatus status) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(Observer);
};
AlsReader();
~AlsReader();
// Adds or removes an observer.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// An observer can call this method to check if ALS has been properly
// initialized and ready to use.
AlsInitStatus GetInitStatus() const;
// Checks if an ALS is enabled, and if the config is valid . Also
// reads ambient light file path.
void Init();
// Sets the task runner for testing purpose.
void SetTaskRunnerForTesting(
scoped_refptr<base::SequencedTaskRunner> task_runner);
// Sets ambient light path for testing purpose and initialize. This will cause
// all the checks to be skipped, i.e. whether ALS is enabled and if config is
// valid.
void InitForTesting(const base::FilePath& ambient_light_path);
base::WeakPtr<AlsReader> AsWeakPtr();
private:
friend class AlsReaderTest;
// Called when we've checked whether ALS is enabled.
void OnAlsEnableCheckDone(bool is_enabled);
// Called when we've checked whether ALS config is valid.
void OnAlsConfigCheckDone(bool is_config_valid);
// Called when we've tried to read ALS path. If |path| is empty, it would
// reschedule another attempt up to |kMaxInitialAttempts|.
void OnAlsPathReadAttempted(const std::string& path);
// Tries to read ALS path.
void RetryAlsPath();
// Notifies all observers with |status_| after AlsReader is initialized.
void OnInitializationComplete();
// Polls ambient light periodically and notifies all observers if a sample is
// read.
void ReadAlsPeriodically();
// This is called after ambient light (represented as |data|) is sampled. It
// parses |data| to int, notifies its observers and starts |als_timer_| for
// next sample.
void OnAlsRead(const std::string& data);
AlsInitStatus status_ = AlsInitStatus::kInProgress;
base::FilePath ambient_light_path_;
int num_failed_initialization_ = 0;
// Timer used to retry initialization and also for periodic ambient light
// sampling.
base::OneShotTimer als_timer_;
scoped_refptr<base::SequencedTaskRunner> als_task_runner_;
base::ObserverList<Observer> observers_;
base::WeakPtrFactory<AlsReader> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(AlsReader);
};
} // namespace auto_screen_brightness
} // namespace power
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_ALS_READER_H_
// Copyright 2018 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/als_reader.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace power {
namespace auto_screen_brightness {
namespace {
class TestObserver : public AlsReader::Observer {
public:
TestObserver() {}
~TestObserver() override = default;
// AlsReader::Observer overrides:
void OnAmbientLightUpdated(const int lux) override {
ambient_light_ = lux;
++num_received_ambient_lights_;
}
void OnAlsReaderInitialized(const AlsReader::AlsInitStatus status) override {
status_ = base::Optional<AlsReader::AlsInitStatus>(status);
}
int get_ambient_light() { return ambient_light_; }
int get_num_received_ambient_lights() { return num_received_ambient_lights_; }
AlsReader::AlsInitStatus get_status() {
CHECK(status_);
return status_.value();
}
private:
int ambient_light_ = -1;
int num_received_ambient_lights_ = 0;
base::Optional<AlsReader::AlsInitStatus> status_;
DISALLOW_COPY_AND_ASSIGN(TestObserver);
};
} // namespace
class AlsReaderTest : public testing::Test {
public:
AlsReaderTest()
: scoped_task_environment_(
std::make_unique<base::test::ScopedTaskEnvironment>(
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME)) {
CHECK(temp_dir_.CreateUniqueTempDir());
ambient_light_path_ = temp_dir_.GetPath().Append("test_als");
als_reader_.SetTaskRunnerForTesting(base::SequencedTaskRunnerHandle::Get());
als_reader_.AddObserver(&test_observer_);
als_reader_.InitForTesting(ambient_light_path_);
}
~AlsReaderTest() override = default;
protected:
void WriteLux(int lux) {
const std::string lux_string = base::IntToString(lux);
const int bytes_written = base::WriteFile(
ambient_light_path_, lux_string.data(), lux_string.size());
ASSERT_EQ(bytes_written, static_cast<int>(lux_string.size()))
<< "Wrote " << bytes_written << " byte(s) instead of "
<< lux_string.size() << " to " << ambient_light_path_.value();
}
base::ScopedTempDir temp_dir_;
base::FilePath ambient_light_path_;
std::unique_ptr<base::test::ScopedTaskEnvironment> scoped_task_environment_;
TestObserver test_observer_;
AlsReader als_reader_;
private:
DISALLOW_COPY_AND_ASSIGN(AlsReaderTest);
};
TEST_F(AlsReaderTest, CheckInitStatusAlsFileFound) {
EXPECT_EQ(AlsReader::AlsInitStatus::kSuccess, als_reader_.GetInitStatus());
}
TEST_F(AlsReaderTest, OnAlsReaderInitialized) {
EXPECT_EQ(AlsReader::AlsInitStatus::kSuccess, test_observer_.get_status());
}
TEST_F(AlsReaderTest, OneAlsValue) {
WriteLux(10);
scoped_task_environment_->RunUntilIdle();
EXPECT_EQ(10, test_observer_.get_ambient_light());
EXPECT_EQ(1, test_observer_.get_num_received_ambient_lights());
}
TEST_F(AlsReaderTest, TwoAlsValues) {
WriteLux(10);
// Ambient light is read immediately after initialization, and then
// periodically every |kAlsPollInterval|. Below we move time for half of
// |kAlsPollInterval| to ensure there is only one reading attempt.
scoped_task_environment_->FastForwardBy(AlsReader::kAlsPollInterval / 2);
EXPECT_EQ(10, test_observer_.get_ambient_light());
EXPECT_EQ(1, test_observer_.get_num_received_ambient_lights());
WriteLux(20);
// Now move time for another |kAlsPollInterval| to trigger another read.
scoped_task_environment_->FastForwardBy(AlsReader::kAlsPollInterval);
EXPECT_EQ(20, test_observer_.get_ambient_light());
EXPECT_EQ(2, test_observer_.get_num_received_ambient_lights());
}
} // namespace auto_screen_brightness
} // namespace power
} // namespace chromeos
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