chromium/chrome/browser/ash/power/auto_screen_brightness/brightness_monitor_impl.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/power/auto_screen_brightness/brightness_monitor_impl.h"

#include <algorithm>
#include <cmath>

#include "ash/constants/ash_features.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "chrome/browser/ash/power/auto_screen_brightness/utils.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "chromeos/dbus/power_manager/backlight.pb.h"

namespace ash {
namespace power {
namespace auto_screen_brightness {

constexpr base::TimeDelta BrightnessMonitorImpl::kBrightnessSampleDelay;

BrightnessMonitorImpl::BrightnessMonitorImpl() = default;
BrightnessMonitorImpl::~BrightnessMonitorImpl() = default;

void BrightnessMonitorImpl::Init() {
  const int brightness_sample_delay_seconds = GetFieldTrialParamByFeatureAsInt(
      features::kAutoScreenBrightness, "brightness_sample_delay_seconds",
      kBrightnessSampleDelay.InSeconds());

  brightness_sample_delay_ =
      brightness_sample_delay_seconds < 0
          ? kBrightnessSampleDelay
          : base::Seconds(brightness_sample_delay_seconds);

  power_manager_client_observation_.Observe(
      chromeos::PowerManagerClient::Get());
}

void BrightnessMonitorImpl::AddObserver(
    BrightnessMonitor::Observer* const observer) {
  DCHECK(observer);
  observers_.AddObserver(observer);
  if (brightness_monitor_status_ != Status::kInitializing) {
    observer->OnBrightnessMonitorInitialized(brightness_monitor_status_ ==
                                             Status::kSuccess);
  }
}

void BrightnessMonitorImpl::RemoveObserver(
    BrightnessMonitor::Observer* const observer) {
  DCHECK(observer);
  observers_.RemoveObserver(observer);
}

void BrightnessMonitorImpl::PowerManagerBecameAvailable(
    const bool service_is_ready) {
  if (!service_is_ready) {
    brightness_monitor_status_ = Status::kDisabled;
    OnInitializationComplete();
    return;
  }
  chromeos::PowerManagerClient::Get()->GetScreenBrightnessPercent(
      base::BindOnce(&BrightnessMonitorImpl::OnReceiveInitialBrightnessPercent,
                     weak_ptr_factory_.GetWeakPtr()));
}

void BrightnessMonitorImpl::ScreenBrightnessChanged(
    const power_manager::BacklightBrightnessChange& change) {
  if (brightness_monitor_status_ != Status::kSuccess) {
    // Either
    // (1). we're waiting for init brightness to come in from powerd, or
    // (2). we've failed to get init brightness from powerd.
    // In any case, we ignore this brightness change.
    return;
  }

  double brightness_percent_received = change.percent();
  if (brightness_percent_received < 0.0 ||
      brightness_percent_received > 100.0) {
    // Brightness should not be outside the range of [0,100]. If it's outside
    // this range after initialization completes successfully, we clip the value
    // instead of throwing it away.
    LogDataError(DataError::kBrightnessPercent);
    brightness_percent_received =
        std::clamp(brightness_percent_received, 0.0, 100.0);
  }

  if (change.cause() ==
      power_manager::BacklightBrightnessChange_Cause_USER_REQUEST) {
    // This is the only brightness change caused by explicit user selection.
    NotifyUserBrightnessChangeRequested();
    user_brightness_percent_ = brightness_percent_received;
    StartBrightnessSampleTimer();
    return;
  }

  // We treat all the other causes as non-user-initiated.
  if (user_brightness_percent_) {
    // If we've received a user-selected brightness change, stop waiting and
    // report the latest |user_brightness_percent_|.
    brightness_sample_timer_.Stop();
    NotifyUserBrightnessChanged();
  }
  stable_brightness_percent_ = brightness_percent_received;
}

base::TimeDelta BrightnessMonitorImpl::GetBrightnessSampleDelayForTesting()
    const {
  return brightness_sample_delay_;
}

void BrightnessMonitorImpl::OnReceiveInitialBrightnessPercent(
    const std::optional<double> brightness_percent) {
  DCHECK_EQ(brightness_monitor_status_, Status::kInitializing);

  if (brightness_percent && *brightness_percent >= 0.0 &&
      *brightness_percent <= 100.0) {
    // Brightness should not be outside the range of [0,100]. If it's outside
    // this range on initialization, then we disable the monitor.
    stable_brightness_percent_ = brightness_percent;
    brightness_monitor_status_ = Status::kSuccess;
  } else {
    brightness_monitor_status_ = Status::kDisabled;
  }

  OnInitializationComplete();
}

void BrightnessMonitorImpl::OnInitializationComplete() {
  DCHECK_NE(brightness_monitor_status_, Status::kInitializing);

  UMA_HISTOGRAM_ENUMERATION("AutoScreenBrightness.BrightnessMonitorStatus",
                            brightness_monitor_status_);

  const bool success = brightness_monitor_status_ == Status::kSuccess;
  for (auto& observer : observers_)
    observer.OnBrightnessMonitorInitialized(success);
}

void BrightnessMonitorImpl::StartBrightnessSampleTimer() {
  // It's ok if the timer is already running, we simply wait a bit longer.
  brightness_sample_timer_.Start(
      FROM_HERE, brightness_sample_delay_, this,
      &BrightnessMonitorImpl::NotifyUserBrightnessChanged);
}

void BrightnessMonitorImpl::NotifyUserBrightnessChanged() {
  if (!user_brightness_percent_) {
    NOTREACHED_IN_MIGRATION()
        << "User brightness adjustment missing on sample timeout";
    return;
  }

  for (auto& observer : observers_) {
    observer.OnUserBrightnessChanged(stable_brightness_percent_.value(),
                                     user_brightness_percent_.value());
  }

  stable_brightness_percent_ = user_brightness_percent_;
  user_brightness_percent_ = std::nullopt;
}

void BrightnessMonitorImpl::NotifyUserBrightnessChangeRequested() {
  for (auto& observer : observers_)
    observer.OnUserBrightnessChangeRequested();
}

}  // namespace auto_screen_brightness
}  // namespace power
}  // namespace ash