| // 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/adapter.h" |
| |
| #include <string> |
| |
| #include "ash/public/cpp/ash_pref_names.h" |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/metrics/field_trial_params.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/time/default_tick_clock.h" |
| #include "chrome/browser/chromeos/power/auto_screen_brightness/utils.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chromeos/constants/chromeos_features.h" |
| #include "chromeos/dbus/power/power_manager_client.h" |
| #include "chromeos/dbus/power_manager/backlight.pb.h" |
| #include "components/prefs/pref_service.h" |
| |
| namespace chromeos { |
| namespace power { |
| namespace auto_screen_brightness { |
| |
| namespace { |
| |
| constexpr double kTol = 1e-10; |
| |
| constexpr double kMaxBrightnessAdjustment = 100; |
| constexpr double kMinBrightnessAdjustment = 1; |
| constexpr size_t kNumBrightnessAdjustmentBuckets = 10; |
| |
| const char* BrightnessChangeCauseToString( |
| Adapter::BrightnessChangeCause cause) { |
| switch (cause) { |
| case Adapter::BrightnessChangeCause::kInitialAlsReceived: |
| return "InitialAlsReceived"; |
| case Adapter::BrightnessChangeCause::kBrightneningThresholdExceeded: |
| return "BrightneningThresholdExceeded"; |
| case Adapter::BrightnessChangeCause::kDarkeningThresholdExceeded: |
| return "DarkeningThresholdExceeded"; |
| // |kImmediateBrightneningThresholdExceeded| and |
| // |kImmediateDarkeningThresholdExceeded| are deprecated, and shouldn't show |
| // up. |
| case Adapter::BrightnessChangeCause:: |
| kImmediateBrightneningThresholdExceeded: |
| case Adapter::BrightnessChangeCause::kImmediateDarkeningThresholdExceeded: |
| return "UnexpectedImmediateTransition"; |
| } |
| return "Unknown"; |
| } |
| |
| // Multiplies input |x| by a factor of 100 and round to the nearest int. |
| int ScaleAndConvertToInt(double x) { |
| return static_cast<int>(x * 100 + 0.5); |
| } |
| |
| // Returns the bucket number from |value| following the exponential bucketing |
| // scheme. Each bucket is inclusive from the left and exclusive from the right. |
| int ExponentialBucketing(double value) { |
| static const double kExponentialBrightnessAdjustmentStepSize = |
| std::log(kMaxBrightnessAdjustment / kMinBrightnessAdjustment) / |
| (kNumBrightnessAdjustmentBuckets - 1); |
| DCHECK_LE(kMinBrightnessAdjustment, value); |
| DCHECK_LE(value, kMaxBrightnessAdjustment); |
| return static_cast<int>(std::log(value / kMinBrightnessAdjustment) / |
| kExponentialBrightnessAdjustmentStepSize); |
| } |
| |
| } // namespace |
| |
| Adapter::Params::Params() = default; |
| |
| Adapter::AdapterDecision::AdapterDecision() = default; |
| |
| Adapter::AdapterDecision::AdapterDecision(const AdapterDecision& decision) = |
| default; |
| |
| Adapter::Adapter(Profile* profile, |
| AlsReader* als_reader, |
| BrightnessMonitor* brightness_monitor, |
| Modeller* modeller, |
| ModelConfigLoader* model_config_loader, |
| MetricsReporter* metrics_reporter) |
| : Adapter(profile, |
| als_reader, |
| brightness_monitor, |
| modeller, |
| model_config_loader, |
| metrics_reporter, |
| base::DefaultTickClock::GetInstance()) {} |
| |
| Adapter::~Adapter() = default; |
| |
| void Adapter::Init() { |
| // Deferred to Init() because it can result in a virtual method being called. |
| power_manager_client_observer_.Add(PowerManagerClient::Get()); |
| } |
| |
| void Adapter::OnAmbientLightUpdated(int lux) { |
| // Ambient light data is only used when adapter is initialized to success. |
| // |log_als_values_| may not be available to use when adapter is being |
| // initialized. |
| if (adapter_status_ != Status::kSuccess) |
| return; |
| |
| DCHECK(log_als_values_); |
| |
| const base::TimeTicks now = tick_clock_->NowTicks(); |
| |
| log_als_values_->SaveToBuffer({ConvertToLog(lux), now}); |
| |
| const AdapterDecision& decision = CanAdjustBrightness(now); |
| |
| if (decision.no_brightness_change_cause) |
| return; |
| |
| DCHECK(decision.brightness_change_cause); |
| DCHECK(decision.log_als_avg_stddev); |
| |
| AdjustBrightness(*decision.brightness_change_cause, |
| decision.log_als_avg_stddev->avg); |
| } |
| |
| void Adapter::OnAlsReaderInitialized(AlsReader::AlsInitStatus status) { |
| DCHECK(!als_init_status_); |
| |
| als_init_status_ = status; |
| als_init_time_ = tick_clock_->NowTicks(); |
| UpdateStatus(); |
| } |
| |
| void Adapter::OnBrightnessMonitorInitialized(bool success) { |
| DCHECK(!brightness_monitor_success_.has_value()); |
| |
| brightness_monitor_success_ = success; |
| UpdateStatus(); |
| } |
| |
| void Adapter::OnUserBrightnessChanged(double old_brightness_percent, |
| double new_brightness_percent) { |
| const auto first_recent_user_brightness_request_time = |
| first_recent_user_brightness_request_time_; |
| const auto decision_at_first_recent_user_brightness_request = |
| decision_at_first_recent_user_brightness_request_; |
| |
| first_recent_user_brightness_request_time_ = base::nullopt; |
| decision_at_first_recent_user_brightness_request_ = base::nullopt; |
| |
| // We skip this notification if adapter hasn't been initialised because its |
| // |params_| may change. We need to log even if adapter is initialized to |
| // disabled. |
| if (adapter_status_ == Status::kInitializing) { |
| return; |
| } |
| |
| // |latest_brightness_change_time_|, |current_brightness_|, |
| // |average_log_ambient_lux_| and thresholds are only needed if adapter is |
| // |kSuccess|. |
| if (adapter_status_ == Status::kSuccess) { |
| if (!decision_at_first_recent_user_brightness_request) { |
| // This should not happen frequently. |
| UMA_HISTOGRAM_BOOLEAN( |
| "AutoScreenBrightness.MissingPriorUserBrightnessRequest", true); |
| return; |
| } |
| DCHECK(first_recent_user_brightness_request_time); |
| LogAdapterDecision(*first_recent_user_brightness_request_time, |
| *decision_at_first_recent_user_brightness_request, |
| old_brightness_percent, new_brightness_percent); |
| |
| const base::Optional<AlsAvgStdDev> log_als_avg_stddev = |
| decision_at_first_recent_user_brightness_request->log_als_avg_stddev; |
| |
| OnBrightnessChanged( |
| *first_recent_user_brightness_request_time, new_brightness_percent, |
| log_als_avg_stddev ? base::Optional<double>(log_als_avg_stddev->avg) |
| : base::nullopt); |
| } |
| |
| if (!metrics_reporter_) |
| return; |
| |
| metrics_reporter_->OnUserBrightnessChangeRequested(); |
| } |
| |
| void Adapter::OnUserBrightnessChangeRequested() { |
| const base::TimeTicks now = tick_clock_->NowTicks(); |
| // We skip this notification if adapter hasn't been initialised (because its |
| // |params_| may change), or, if adapter is disabled (because adapter won't |
| // change brightness anyway). |
| if (adapter_status_ != Status::kSuccess) { |
| // Set |first_recent_user_brightness_request_time_| if not already set, so |
| // that it won't be reset. |
| if (!first_recent_user_brightness_request_time_) |
| first_recent_user_brightness_request_time_ = now; |
| return; |
| } |
| |
| if (!first_recent_user_brightness_request_time_) { |
| DCHECK(log_als_values_); |
| // Check what model would say and also get latest AlsAvgStdDev. |
| decision_at_first_recent_user_brightness_request_ = |
| CanAdjustBrightness(now); |
| first_recent_user_brightness_request_time_ = now; |
| } |
| |
| if (params_.user_adjustment_effect != UserAdjustmentEffect::kContinueAuto) { |
| // Adapter will stop making brightness adjustment until suspend/resume or |
| // when browser restarts. |
| adapter_disabled_by_user_adjustment_ = true; |
| } |
| } |
| |
| void Adapter::OnModelTrained(const MonotoneCubicSpline& brightness_curve) { |
| // It's ok to record brightness curve even when adapter is not completely |
| // initialized. But we stop recording curves if we know adapter is disabled. |
| if (adapter_status_ == Status::kDisabled) |
| return; |
| |
| model_.personal_curve = brightness_curve; |
| ++model_.iteration_count; |
| } |
| |
| void Adapter::OnModelInitialized(const Model& model) { |
| DCHECK(!model_initialized_); |
| |
| model_initialized_ = true; |
| model_ = model; |
| |
| UpdateStatus(); |
| } |
| |
| void Adapter::OnModelConfigLoaded(base::Optional<ModelConfig> model_config) { |
| DCHECK(!model_config_exists_.has_value()); |
| |
| model_config_exists_ = model_config.has_value(); |
| |
| if (model_config_exists_.value()) { |
| InitParams(model_config.value()); |
| } |
| |
| UpdateStatus(); |
| } |
| |
| void Adapter::PowerManagerBecameAvailable(bool service_is_ready) { |
| power_manager_service_available_ = service_is_ready; |
| UpdateStatus(); |
| } |
| |
| void Adapter::SuspendDone(const base::TimeDelta& /* sleep_duration */) { |
| // We skip this notification if adapter hasn't been initialised (because its |
| // |params_| may change), or, if adapter is disabled (because adapter won't |
| // change brightness anyway). |
| if (adapter_status_ != Status::kSuccess) |
| return; |
| |
| if (params_.user_adjustment_effect == UserAdjustmentEffect::kPauseAuto) |
| adapter_disabled_by_user_adjustment_ = false; |
| } |
| |
| Adapter::Status Adapter::GetStatusForTesting() const { |
| return adapter_status_; |
| } |
| |
| bool Adapter::IsAppliedForTesting() const { |
| return (adapter_status_ == Status::kSuccess && |
| !adapter_disabled_by_user_adjustment_); |
| } |
| |
| base::Optional<MonotoneCubicSpline> Adapter::GetGlobalCurveForTesting() const { |
| return model_.global_curve; |
| } |
| |
| base::Optional<MonotoneCubicSpline> Adapter::GetPersonalCurveForTesting() |
| const { |
| return model_.personal_curve; |
| } |
| |
| base::Optional<AlsAvgStdDev> Adapter::GetAverageAmbientWithStdDevForTesting( |
| base::TimeTicks now) { |
| DCHECK(log_als_values_); |
| return log_als_values_->AverageAmbientWithStdDev(now); |
| } |
| |
| double Adapter::GetBrighteningThresholdForTesting() const { |
| return *brightening_threshold_; |
| } |
| |
| double Adapter::GetDarkeningThresholdForTesting() const { |
| return *darkening_threshold_; |
| } |
| |
| base::Optional<double> Adapter::GetCurrentAvgLogAlsForTesting() const { |
| return average_log_ambient_lux_; |
| } |
| |
| std::unique_ptr<Adapter> Adapter::CreateForTesting( |
| Profile* profile, |
| AlsReader* als_reader, |
| BrightnessMonitor* brightness_monitor, |
| Modeller* modeller, |
| ModelConfigLoader* model_config_loader, |
| MetricsReporter* metrics_reporter, |
| const base::TickClock* tick_clock) { |
| return base::WrapUnique(new Adapter(profile, als_reader, brightness_monitor, |
| modeller, model_config_loader, |
| metrics_reporter, tick_clock)); |
| } |
| |
| Adapter::Adapter(Profile* profile, |
| AlsReader* als_reader, |
| BrightnessMonitor* brightness_monitor, |
| Modeller* modeller, |
| ModelConfigLoader* model_config_loader, |
| MetricsReporter* metrics_reporter, |
| const base::TickClock* tick_clock) |
| : profile_(profile), |
| metrics_reporter_(metrics_reporter), |
| tick_clock_(tick_clock) { |
| DCHECK(profile); |
| DCHECK(als_reader); |
| DCHECK(brightness_monitor); |
| DCHECK(modeller); |
| DCHECK(model_config_loader); |
| |
| als_reader_observer_.Add(als_reader); |
| brightness_monitor_observer_.Add(brightness_monitor); |
| modeller_observer_.Add(modeller); |
| model_config_loader_observer_.Add(model_config_loader); |
| } |
| |
| void Adapter::InitParams(const ModelConfig& model_config) { |
| if (!base::FeatureList::IsEnabled(features::kAutoScreenBrightness)) { |
| adapter_status_ = Status::kDisabled; |
| return; |
| } |
| |
| params_.metrics_key = model_config.metrics_key; |
| params_.brightening_log_lux_threshold = GetFieldTrialParamByFeatureAsDouble( |
| features::kAutoScreenBrightness, "brightening_log_lux_threshold", |
| params_.brightening_log_lux_threshold); |
| |
| params_.darkening_log_lux_threshold = GetFieldTrialParamByFeatureAsDouble( |
| features::kAutoScreenBrightness, "darkening_log_lux_threshold", |
| params_.darkening_log_lux_threshold); |
| |
| params_.stabilization_threshold = GetFieldTrialParamByFeatureAsDouble( |
| features::kAutoScreenBrightness, "stabilization_threshold", |
| params_.stabilization_threshold); |
| |
| const int model_curve = base::GetFieldTrialParamByFeatureAsInt( |
| features::kAutoScreenBrightness, "model_curve", 2); |
| if (model_curve < 0 || model_curve > 2) { |
| adapter_status_ = Status::kDisabled; |
| LogParameterError(ParameterError::kAdapterError); |
| return; |
| } |
| params_.model_curve = static_cast<ModelCurve>(model_curve); |
| params_.auto_brightness_als_horizon = base::TimeDelta::FromSeconds( |
| model_config.auto_brightness_als_horizon_seconds); |
| log_als_values_ = std::make_unique<AmbientLightSampleBuffer>( |
| params_.auto_brightness_als_horizon); |
| |
| // TODO(jiameng): move this to device config once we complete experiments. |
| if (model_config.metrics_key == "atlas") { |
| params_.user_adjustment_effect = UserAdjustmentEffect::kContinueAuto; |
| } |
| |
| const int user_adjustment_effect_as_int = GetFieldTrialParamByFeatureAsInt( |
| features::kAutoScreenBrightness, "user_adjustment_effect", |
| static_cast<int>(params_.user_adjustment_effect)); |
| if (user_adjustment_effect_as_int < 0 || user_adjustment_effect_as_int > 2) { |
| LogParameterError(ParameterError::kAdapterError); |
| return; |
| } |
| params_.user_adjustment_effect = |
| static_cast<UserAdjustmentEffect>(user_adjustment_effect_as_int); |
| |
| UMA_HISTOGRAM_ENUMERATION("AutoScreenBrightness.UserAdjustmentEffect", |
| params_.user_adjustment_effect); |
| } |
| |
| void Adapter::UpdateStatus() { |
| if (adapter_status_ != Status::kInitializing) |
| return; |
| |
| if (!als_init_status_) |
| return; |
| |
| const bool als_success = |
| *als_init_status_ == AlsReader::AlsInitStatus::kSuccess; |
| if (!als_success) { |
| adapter_status_ = Status::kDisabled; |
| SetMetricsReporterDeviceClass(); |
| return; |
| } |
| |
| if (!brightness_monitor_success_.has_value()) |
| return; |
| |
| if (!*brightness_monitor_success_) { |
| adapter_status_ = Status::kDisabled; |
| SetMetricsReporterDeviceClass(); |
| return; |
| } |
| |
| if (!model_initialized_) |
| return; |
| |
| if (!model_.global_curve) { |
| adapter_status_ = Status::kDisabled; |
| SetMetricsReporterDeviceClass(); |
| return; |
| } |
| |
| if (!power_manager_service_available_.has_value()) |
| return; |
| |
| if (!*power_manager_service_available_) { |
| adapter_status_ = Status::kDisabled; |
| SetMetricsReporterDeviceClass(); |
| return; |
| } |
| |
| if (!model_config_exists_.has_value()) |
| return; |
| |
| if (!model_config_exists_.value()) { |
| adapter_status_ = Status::kDisabled; |
| SetMetricsReporterDeviceClass(); |
| return; |
| } |
| |
| adapter_status_ = Status::kSuccess; |
| SetMetricsReporterDeviceClass(); |
| } |
| |
| void Adapter::SetMetricsReporterDeviceClass() { |
| if (!metrics_reporter_) |
| return; |
| |
| DCHECK_NE(adapter_status_, Status::kInitializing); |
| DCHECK(als_init_status_); |
| |
| switch (*als_init_status_) { |
| case AlsReader::AlsInitStatus::kSuccess: |
| if (params_.metrics_key == "eve") { |
| metrics_reporter_->SetDeviceClass(MetricsReporter::DeviceClass::kEve); |
| return; |
| } |
| if (params_.metrics_key == "atlas") { |
| metrics_reporter_->SetDeviceClass(MetricsReporter::DeviceClass::kAtlas); |
| return; |
| } |
| metrics_reporter_->SetDeviceClass( |
| MetricsReporter::DeviceClass::kSupportedAls); |
| return; |
| case AlsReader::AlsInitStatus::kDisabled: |
| case AlsReader::AlsInitStatus::kMissingPath: |
| metrics_reporter_->SetDeviceClass(MetricsReporter::DeviceClass::kNoAls); |
| return; |
| case AlsReader::AlsInitStatus::kIncorrectConfig: |
| metrics_reporter_->SetDeviceClass( |
| MetricsReporter::DeviceClass::kUnsupportedAls); |
| return; |
| case AlsReader::AlsInitStatus::kInProgress: |
| NOTREACHED() << "ALS should have been initialized with a valid value."; |
| } |
| } |
| |
| Adapter::AdapterDecision Adapter::CanAdjustBrightness(base::TimeTicks now) { |
| DCHECK_EQ(adapter_status_, Status::kSuccess); |
| DCHECK(log_als_values_); |
| DCHECK(!als_init_time_.is_null()); |
| |
| AdapterDecision decision; |
| const base::Optional<AlsAvgStdDev> log_als_avg_stddev = |
| log_als_values_->AverageAmbientWithStdDev(now); |
| decision.log_als_avg_stddev = log_als_avg_stddev; |
| |
| // User has previously manually changed brightness and it (at least |
| // temporarily) stopped the adapter from operating. |
| if (adapter_disabled_by_user_adjustment_) { |
| decision.no_brightness_change_cause = |
| NoBrightnessChangeCause::kDisabledByUser; |
| return decision; |
| } |
| |
| // Do not change brightness if it's set by the policy, but do not completely |
| // disable the model as the policy could change. |
| if (profile_->GetPrefs()->GetInteger( |
| ash::prefs::kPowerAcScreenBrightnessPercent) >= 0 || |
| profile_->GetPrefs()->GetInteger( |
| ash::prefs::kPowerBatteryScreenBrightnessPercent) >= 0) { |
| decision.no_brightness_change_cause = |
| NoBrightnessChangeCause::kBrightnessSetByPolicy; |
| return decision; |
| } |
| |
| if (params_.model_curve == ModelCurve::kPersonal && !model_.personal_curve) { |
| decision.no_brightness_change_cause = |
| NoBrightnessChangeCause::kMissingPersonalCurve; |
| return decision; |
| } |
| |
| // Wait until we've had enough ALS data to calc avg. |
| if (now - als_init_time_ < params_.auto_brightness_als_horizon) { |
| decision.no_brightness_change_cause = |
| NoBrightnessChangeCause::kWaitingForInitialAls; |
| return decision; |
| } |
| |
| // Check if we've waited long enough from previous brightness change (either |
| // by user or by model). |
| if (!latest_brightness_change_time_.is_null() && |
| now - latest_brightness_change_time_ < |
| params_.auto_brightness_als_horizon) { |
| decision.no_brightness_change_cause = |
| NoBrightnessChangeCause::kWaitingForAvgHorizon; |
| return decision; |
| } |
| |
| if (!log_als_avg_stddev) { |
| decision.no_brightness_change_cause = |
| NoBrightnessChangeCause::kMissingAlsData; |
| return decision; |
| } |
| |
| if (!average_log_ambient_lux_) { |
| // Either |
| // 1. brightness hasn't been changed, or, |
| // 2. brightness was changed by the user but there wasn't any ALS data. This |
| // case should be rare. |
| // In either case, we change brightness as soon as we have brightness. |
| decision.brightness_change_cause = |
| BrightnessChangeCause::kInitialAlsReceived; |
| return decision; |
| } |
| |
| // The following thresholds should have been set last time when brightness was |
| // changed. |
| DCHECK(brightening_threshold_); |
| DCHECK(darkening_threshold_); |
| |
| if (log_als_avg_stddev->avg > *brightening_threshold_) { |
| if (log_als_avg_stddev->stddev <= params_.brightening_log_lux_threshold * |
| params_.stabilization_threshold) { |
| decision.brightness_change_cause = |
| BrightnessChangeCause::kBrightneningThresholdExceeded; |
| return decision; |
| } |
| decision.no_brightness_change_cause = |
| NoBrightnessChangeCause::kFluctuatingAlsIncrease; |
| return decision; |
| } |
| |
| if (log_als_avg_stddev->avg < *darkening_threshold_) { |
| if (log_als_avg_stddev->stddev <= |
| params_.darkening_log_lux_threshold * params_.stabilization_threshold) { |
| decision.brightness_change_cause = |
| BrightnessChangeCause::kDarkeningThresholdExceeded; |
| return decision; |
| } |
| decision.no_brightness_change_cause = |
| NoBrightnessChangeCause::kFluctuatingAlsDecrease; |
| return decision; |
| } |
| |
| decision.no_brightness_change_cause = |
| NoBrightnessChangeCause::kMinimalAlsChange; |
| return decision; |
| } |
| |
| void Adapter::AdjustBrightness(BrightnessChangeCause cause, |
| double log_als_avg) { |
| const double brightness = GetBrightnessBasedOnAmbientLogLux(log_als_avg); |
| |
| power_manager::SetBacklightBrightnessRequest request; |
| request.set_percent(brightness); |
| request.set_transition( |
| power_manager::SetBacklightBrightnessRequest_Transition_SLOW); |
| request.set_cause(power_manager::SetBacklightBrightnessRequest_Cause_MODEL); |
| PowerManagerClient::Get()->SetScreenBrightness(request); |
| |
| const base::TimeTicks brightness_change_time = tick_clock_->NowTicks(); |
| if (!latest_model_brightness_change_time_.is_null()) { |
| UMA_HISTOGRAM_LONG_TIMES( |
| "AutoScreenBrightness.BrightnessChange.ElapsedTime", |
| brightness_change_time - latest_model_brightness_change_time_); |
| } |
| latest_model_brightness_change_time_ = brightness_change_time; |
| if (current_brightness_) { |
| model_brightness_change_ = brightness - *current_brightness_; |
| } |
| |
| UMA_HISTOGRAM_ENUMERATION("AutoScreenBrightness.BrightnessChange.Cause", |
| cause); |
| |
| WriteLogMessages(log_als_avg, brightness, cause); |
| model_brightness_change_counter_++; |
| |
| OnBrightnessChanged(brightness_change_time, brightness, log_als_avg); |
| } |
| |
| double Adapter::GetBrightnessBasedOnAmbientLogLux( |
| double ambient_log_lux) const { |
| DCHECK_EQ(adapter_status_, Status::kSuccess); |
| switch (params_.model_curve) { |
| case ModelCurve::kGlobal: |
| return model_.global_curve->Interpolate(ambient_log_lux); |
| case ModelCurve::kPersonal: |
| DCHECK(model_.personal_curve); |
| return model_.personal_curve->Interpolate(ambient_log_lux); |
| default: |
| // We use the latest curve available. |
| if (model_.personal_curve) |
| return model_.personal_curve->Interpolate(ambient_log_lux); |
| return model_.global_curve->Interpolate(ambient_log_lux); |
| } |
| } |
| |
| void Adapter::OnBrightnessChanged(base::TimeTicks now, |
| double new_brightness_percent, |
| base::Optional<double> new_log_als) { |
| DCHECK_NE(adapter_status_, Status::kInitializing); |
| |
| current_brightness_ = new_brightness_percent; |
| latest_brightness_change_time_ = now; |
| |
| UMA_HISTOGRAM_BOOLEAN("AutoScreenBrightness.MissingAlsWhenBrightnessChanged", |
| !new_log_als); |
| |
| if (!new_log_als) |
| return; |
| |
| // Update |average_log_ambient_lux_| with the new reference value. Brightness |
| // will be changed by the model if next log-avg ALS value goes outside of the |
| // range |
| // [|darkening_threshold_|, |brightening_threshold_|]. |
| // Thresholds in |params_| are absolute values to be added/subtracted from |
| // the reference values. Log-avg can be negative. |
| average_log_ambient_lux_ = new_log_als; |
| brightening_threshold_ = *new_log_als + params_.brightening_log_lux_threshold; |
| darkening_threshold_ = *new_log_als - params_.darkening_log_lux_threshold; |
| } |
| |
| void Adapter::WriteLogMessages(double new_log_als, |
| double new_brightness, |
| BrightnessChangeCause cause) const { |
| DCHECK_EQ(adapter_status_, Status::kSuccess); |
| const std::string old_log_als = |
| average_log_ambient_lux_ |
| ? base::StringPrintf("%.4f", average_log_ambient_lux_.value()) + "->" |
| : ""; |
| |
| const std::string old_brightness = |
| current_brightness_ |
| ? base::StringPrintf("%.4f", current_brightness_.value()) + "%->" |
| : ""; |
| |
| VLOG(1) << "Screen brightness change #" << model_brightness_change_counter_ |
| << ": " |
| << "brightness=" << old_brightness |
| << base::StringPrintf("%.4f", new_brightness) << "%" |
| << " cause=" << BrightnessChangeCauseToString(cause) |
| << " log_als=" << old_log_als |
| << base::StringPrintf("%.4f", new_log_als); |
| } |
| |
| void Adapter::LogAdapterDecision( |
| base::TimeTicks first_recent_user_brightness_request_time, |
| const AdapterDecision& decision, |
| double old_brightness_percent, |
| double new_brightness_percent) const { |
| const bool was_previous_change_by_model = |
| !latest_model_brightness_change_time_.is_null() && |
| latest_brightness_change_time_ == latest_model_brightness_change_time_; |
| |
| const bool is_decision_brightness_change = |
| decision.brightness_change_cause.has_value(); |
| DCHECK_NE(is_decision_brightness_change, |
| decision.no_brightness_change_cause.has_value()); |
| |
| const std::string histogram_prefix = |
| std::string("AutoScreenBrightness.AdapterDecisionAtUserChange.") + |
| (is_decision_brightness_change ? "" : "No") + "BrightnessChange."; |
| |
| // What we log depends on whether the decision is to change or not to change. |
| if (is_decision_brightness_change) { |
| base::UmaHistogramEnumeration(histogram_prefix + "Cause", |
| *decision.brightness_change_cause); |
| } else { |
| const NoBrightnessChangeCause no_brightness_change_cause = |
| *decision.no_brightness_change_cause; |
| base::UmaHistogramEnumeration(histogram_prefix + "Cause", |
| no_brightness_change_cause); |
| |
| // If previous change was triggered by the model, we log additional metrics. |
| if (was_previous_change_by_model) { |
| UMA_HISTOGRAM_LONG_TIMES( |
| "AutoScreenBrightness.ElapsedTimeBetweenModelAndUserAdjustments", |
| first_recent_user_brightness_request_time - |
| latest_model_brightness_change_time_); |
| |
| // If we have a recorded model change, compare it with user change and |
| // log. |
| if (model_brightness_change_ && |
| (no_brightness_change_cause == |
| NoBrightnessChangeCause::kMinimalAlsChange || |
| no_brightness_change_cause == |
| NoBrightnessChangeCause::kWaitingForAvgHorizon)) { |
| const double user_brightness_adj = |
| new_brightness_percent - old_brightness_percent; |
| // Whether they are both +ve or negative. |
| const bool user_model_same_direction = |
| *model_brightness_change_ * user_brightness_adj >= 0; |
| |
| const int user_brightness_adj_bucket = |
| ExponentialBucketing(std::max(1.0, std::abs(user_brightness_adj))); |
| |
| const int model_brightness_adj_bucket = ExponentialBucketing( |
| std::max(1.0, std::abs(*model_brightness_change_))); |
| |
| const int logged_value = |
| user_brightness_adj_bucket * kNumBrightnessAdjustmentBuckets + |
| model_brightness_adj_bucket; |
| |
| const std::string prefix = "AutoScreenBrightness."; |
| base::UmaHistogramExactLinear( |
| prefix + (user_model_same_direction ? "Same" : "Opposite") + |
| ".UserModelBrightnessAdjustments", |
| logged_value, 100 /* value_max */); |
| } |
| } |
| } |
| |
| // Log ALS delta and std-dev if they exist. |
| if (decision.log_als_avg_stddev) { |
| const int logged_stddev = |
| ScaleAndConvertToInt(decision.log_als_avg_stddev->stddev); |
| if (average_log_ambient_lux_) { |
| const double log_als_diff = |
| decision.log_als_avg_stddev->avg - *average_log_ambient_lux_; |
| if (std::abs(log_als_diff) < kTol) |
| return; |
| |
| const std::string dir = log_als_diff > 0 ? "Brighten" : "Darken"; |
| |
| const int logged_value = ScaleAndConvertToInt(std::abs(log_als_diff)); |
| base::UmaHistogramCounts1000(histogram_prefix + dir + ".AlsDelta", |
| logged_value); |
| base::UmaHistogramCounts1000(histogram_prefix + dir + ".AlsStd", |
| logged_stddev); |
| return; |
| } |
| |
| base::UmaHistogramCounts1000(histogram_prefix + "Unknown.AlsStd", |
| logged_stddev); |
| } |
| } |
| |
| } // namespace auto_screen_brightness |
| } // namespace power |
| } // namespace chromeos |