blob: d28acc72911620f2e1b9d8a2d2c6d3a90fe2c70b [file] [log] [blame]
// Copyright 2019 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/model_config_loader_impl.h"
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/json/json_value_converter.h"
#include "base/logging.h"
#include "base/metrics/field_trial_params.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/task/post_task.h"
#include "base/task_runner_util.h"
#include "base/values.h"
#include "chromeos/constants/chromeos_features.h"
#include "content/public/browser/browser_thread.h"
namespace chromeos {
namespace power {
namespace auto_screen_brightness {
namespace {
// Reads string content from |model_params_path|, which should exist.
// This should run in another thread to be non-blocking to the main thread (if
// |is_testing| is false).
std::string LoadModelParamsFromDisk(const base::FilePath& model_params_path,
bool is_testing) {
DCHECK(is_testing ||
!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
DCHECK(base::PathExists(model_params_path));
std::string content;
if (!base::ReadFileToString(model_params_path, &content)) {
return std::string();
}
return content;
}
// ModelConfigFromJson and GlobalCurveFromJson are used to hold JSON-parsed
// params.
struct GlobalCurveFromJson {
std::vector<std::unique_ptr<double>> log_lux;
std::vector<std::unique_ptr<double>> brightness;
static void RegisterJSONConverter(
base::JSONValueConverter<GlobalCurveFromJson>* converter) {
converter->RegisterRepeatedDouble("log_lux", &GlobalCurveFromJson::log_lux);
converter->RegisterRepeatedDouble("brightness",
&GlobalCurveFromJson::brightness);
}
};
struct ModelConfigFromJson {
double auto_brightness_als_horizon_seconds;
GlobalCurveFromJson global_curve;
std::string metrics_key;
double model_als_horizon_seconds;
ModelConfigFromJson()
: auto_brightness_als_horizon_seconds(-1.0),
model_als_horizon_seconds(-1.0) {}
static void RegisterJSONConverter(
base::JSONValueConverter<ModelConfigFromJson>* converter) {
converter->RegisterDoubleField(
"auto_brightness_als_horizon_seconds",
&ModelConfigFromJson::auto_brightness_als_horizon_seconds);
converter->RegisterNestedField("global_curve",
&ModelConfigFromJson::global_curve);
converter->RegisterStringField("metrics_key",
&ModelConfigFromJson::metrics_key);
converter->RegisterDoubleField(
"model_als_horizon_seconds",
&ModelConfigFromJson::model_als_horizon_seconds);
}
};
} // namespace
ModelConfigLoaderImpl::ModelConfigLoaderImpl()
: ModelConfigLoaderImpl(
base::FilePath(
"/usr/share/chromeos-assets/autobrightness/model_params.json"),
base::CreateSequencedTaskRunnerWithTraits(
{base::TaskPriority::USER_VISIBLE, base::MayBlock(),
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}),
false /* is_testing */) {}
ModelConfigLoaderImpl::~ModelConfigLoaderImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void ModelConfigLoaderImpl::AddObserver(ModelConfigLoader::Observer* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(observer);
observers_.AddObserver(observer);
if (is_initialized_) {
observer->OnModelConfigLoaded(
is_model_config_valid_ ? base::Optional<ModelConfig>(model_config_)
: base::nullopt);
}
}
void ModelConfigLoaderImpl::RemoveObserver(
ModelConfigLoader::Observer* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(observer);
observers_.RemoveObserver(observer);
}
std::unique_ptr<ModelConfigLoaderImpl> ModelConfigLoaderImpl::CreateForTesting(
const base::FilePath& model_params_path,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) {
return base::WrapUnique(new ModelConfigLoaderImpl(
model_params_path, blocking_task_runner, true /* is_testing */));
}
ModelConfigLoaderImpl::ModelConfigLoaderImpl(
const base::FilePath& model_params_path,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
bool is_testing)
: model_params_path_(model_params_path),
blocking_task_runner_(blocking_task_runner),
is_testing_(is_testing),
weak_ptr_factory_(this) {
Init();
}
void ModelConfigLoaderImpl::Init() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!base::PathExists(model_params_path_)) {
// Allow experiment flags to provide configs if there isn't any config from
// the disk.
InitFromParams();
return;
}
base::PostTaskAndReplyWithResult(
blocking_task_runner_.get(), FROM_HERE,
base::BindOnce(&LoadModelParamsFromDisk, model_params_path_, is_testing_),
base::BindOnce(&ModelConfigLoaderImpl::OnModelParamsLoadedFromDisk,
weak_ptr_factory_.GetWeakPtr()));
}
void ModelConfigLoaderImpl::InitFromParams() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
model_config_.auto_brightness_als_horizon_seconds =
GetFieldTrialParamByFeatureAsInt(
features::kAutoScreenBrightness,
"auto_brightness_als_horizon_seconds",
model_config_.auto_brightness_als_horizon_seconds);
model_config_.model_als_horizon_seconds = GetFieldTrialParamByFeatureAsInt(
features::kAutoScreenBrightness, "model_als_horizon_seconds",
model_config_.model_als_horizon_seconds);
const std::string global_curve = GetFieldTrialParamValueByFeature(
features::kAutoScreenBrightness, "global_curve");
if (global_curve.empty()) {
OnInitializationComplete();
return;
}
base::StringPairs key_value_pairs;
if (!base::SplitStringIntoKeyValuePairs(global_curve, ':', ',',
&key_value_pairs)) {
OnInitializationComplete();
return;
}
std::vector<double> log_lux;
std::vector<double> brightness;
for (const auto& key_value : key_value_pairs) {
double log_lux_val = 0;
if (!base::StringToDouble(key_value.first, &log_lux_val)) {
OnInitializationComplete();
return;
}
double brightness_val = 0;
if (!base::StringToDouble(key_value.second, &brightness_val)) {
OnInitializationComplete();
return;
}
log_lux.push_back(log_lux_val);
brightness.push_back(brightness_val);
}
model_config_.log_lux = log_lux;
model_config_.brightness = brightness;
OnInitializationComplete();
}
void ModelConfigLoaderImpl::OnModelParamsLoadedFromDisk(
const std::string& content) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (content.empty()) {
InitFromParams();
return;
}
base::Optional<base::Value> value = base::JSONReader::Read(content);
if (!value) {
InitFromParams();
return;
}
base::JSONValueConverter<ModelConfigFromJson> converter;
ModelConfigFromJson loaded_model_configs;
if (!converter.Convert(*value, &loaded_model_configs)) {
InitFromParams();
return;
}
model_config_.auto_brightness_als_horizon_seconds =
loaded_model_configs.auto_brightness_als_horizon_seconds;
std::vector<double> log_lux;
for (const auto& log_lux_val : loaded_model_configs.global_curve.log_lux) {
DCHECK(log_lux_val);
model_config_.log_lux.push_back(*log_lux_val);
}
std::vector<double> brightness;
for (const auto& brightness_val :
loaded_model_configs.global_curve.brightness) {
DCHECK(brightness_val);
model_config_.brightness.push_back(*brightness_val);
}
model_config_.metrics_key = loaded_model_configs.metrics_key;
model_config_.model_als_horizon_seconds =
loaded_model_configs.model_als_horizon_seconds;
InitFromParams();
}
void ModelConfigLoaderImpl::OnInitializationComplete() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
is_model_config_valid_ = IsValidModelConfig(model_config_);
is_initialized_ = true;
for (auto& observer : observers_) {
observer.OnModelConfigLoaded(
is_model_config_valid_ ? base::Optional<ModelConfig>(model_config_)
: base::nullopt);
}
}
} // namespace auto_screen_brightness
} // namespace power
} // namespace chromeos